005-spring cache-原理、缓存AOP机制、Spring Cache抽象集成机制、springboot自动配置机制

一、概述

  缓存抽象提供了多种存储集成。要使用它们,需要简单地声明一个适当的CacheManager - 一个控制和管理Caches的实体,可用于检索这些实体以进行存储。

  springboot cache主要分为两部分:spring cache和springboot cache部分。

二、spring cache

  主要位于如下包下:

    

三、spring boot cache

    

四、原理

  SpringCache使用Spring AOP来实现,当我们在Configuration类打上@EnableCaching注释时,该标注通过ImportSelector机制启动AbstractAdvisorAutoProxyCreator的一个实例,该实例本身是一个Ordered BeanPostProcessor,BeanPostProcessor的作用是在bean创建及初始化的前后对其进行一些额外的操作(包括创建代理),而Spring AOP就是通过AbstractAdvisorAutoProxyCreator来实现的。《BeanPostProcessor加载次序及其对Bean造成的影响分析

  当在Configuration类打上@EnableCaching注释时,做的第一件事情就是启用Spring AOP机制。

4.1、aop机制

  从Spring2.0开始,引入了AspectJ的注释,使得定义切面变得非常简单。Spring采用AspectJ注释对POJO进行标注,从而定义一个包含切点信息和增强横切面逻辑的切面,而Spring的底层负责将这个切面织入到匹配的目标bean中。

  AspectJ注释使用AspectJ切点表达式语法进行切点的定义,可以通过切点函数、运算符、通配符等高级功能进行切点定义,拥有强大的连接点描述能力。

  在运行中,当创建并初始化一个bean的时候,Spring会判断这个bean上打的注释,若是通过Aspect注释定义的切点,则将切点信息以及增强逻辑织入到这个新创建的bean中,切点及增强逻辑会放置在一个Advisor中,而每个bean有可能有很多个Advisor。织入的方式有两种:动态代理和静态织入。在此我们使用动态代理的方式。

  而在实际调用到某个方法时,其实是通过创建代理时设置的回调函数作为调用入口(JdkProxy和cglib大同小异),其中会过滤出可以应用于本方法的Advisor并生成Interceptor或者InterceptorAndDynamicMethodMatcher数组,以类似于FilterChain的方式按照的顺序来执行。

interceptor1.pre()
interceptor2.pre()
.....
interceptorn.pre()
method.invoke()
interceptorn.aft()
......
interceptor2.aft()
interceptor1.aft()

 

Spring AOP整体实现逻辑如下:

  启动时收集beanfactory中定义的所有的切点Pointcuts及通知函数Advices(拦截器函数),将其封装为切面Advisors。

  创建bean时,判断是否有针对该bean的Advisors,如果有,则为该bean创建代理,并将合适的Advisors封装进代理中。

  在执行过程中,当调用到该bean的方法method时,其实入口是代理类的回调函数。在回调函数中将使用于本方法的Advice(Interceptor)和被调用函数method封装成一个JoinPoint(运行时连接点)使用类似FilterChain的模式来执行拦截器链及方法调用。《SpringAOP基本概念及实现原理(三)

4.2、Spring Cache抽象集成机制

  SpringCache使用Spring AOP面向切面编程的机制来实现,当我们在Configuration类打上@EnableCaching注释时,除了启动Spring AOP机制,引入的另一个类ProxyCachingConfiguration就是SpringCache具体实现相关bean的配置类。其代码如下:

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

    @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
        BeanFactoryCacheOperationSourceAdvisor advisor =
                new BeanFactoryCacheOperationSourceAdvisor();
        advisor.setCacheOperationSource(cacheOperationSource());
        advisor.setAdvice(cacheInterceptor());
        if (this.enableCaching != null) {
            advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
        }
        return advisor;
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheOperationSource cacheOperationSource() {
        return new AnnotationCacheOperationSource();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheInterceptor cacheInterceptor() {
        CacheInterceptor interceptor = new CacheInterceptor();
        interceptor.setCacheOperationSources(cacheOperationSource());
        if (this.cacheResolver != null) {
            interceptor.setCacheResolver(this.cacheResolver);
        }
        else if (this.cacheManager != null) {
            interceptor.setCacheManager(this.cacheManager);
        }
        if (this.keyGenerator != null) {
            interceptor.setKeyGenerator(this.keyGenerator);
        }
        if (this.errorHandler != null) {
            interceptor.setErrorHandler(this.errorHandler);
        }
        return interceptor;
    }
}
View Code

其中配置了三个bean:BeanFactoryCacheOperationSourceAdvisor、AnnotationCacheOperationSource、CacheInterceptor。

  AnnotationCacheOperationSource的主要作用是获取定义在类和方法上的SpringCache相关的标注并将其转换为对应的CacheOperation属性。

  BeanFactoryCacheOperationSourceAdvisor是一个PointcutAdvisor,是SpringCache使用Spring AOP机制的关键所在,该advisor会织入到需要执行缓存操作的bean的增强代理中形成一个切面。并在方法调用时在该切面上执行拦截器CacheInterceptor的业务逻辑。

  CacheInterceptor是一个拦截器,当方法调用时碰到了BeanFactoryCacheOperationSourceAdvisor定义的切面,就会执行CacheInterceptor的业务逻辑,该业务逻辑就是缓存的核心业务逻辑。

4.2.1、AnnotationCacheOperationSource

  继承路径是:AnnotationCacheOperationSource->AbstractFallbackCacheOperationSource->CacheOperationSource

  1、CacheOperationSource接口定义了一个方法:

Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass);

  该方法用于根据指定类上的指定方法上打的SpringCache注释来得到对应的CacheOperation集合。

  2、AbstractFallbackCacheOperationSource是CacheOperationSource的抽象实现类,采用模板模式将获取某类的某方法上的CacheOperation的业务流程固化。

  该固化的流程可将方法上的属性缓存,并实现了一个获取CacheOperation的fallback策略,执行的顺序为:1目标方法、2目标类、3声明方法、4声明类/接口。

  当方法被调用过一次之后,其上的属性就会被缓存。它提供了两个抽象模板方法,供具体的子类类来实现:

protected abstract Collection<CacheOperation> findCacheOperations(Method method);
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);

  3、AnnotationCacheOperationSource内部持有一个Set<CacheAnnotaionParser>的集合,默认包含SpringCacheAnnotationParser,并使用SpringCacheAnnotationParser来实现AbstractFallbackCacheOperationSource定义的两个抽象方法:

    @Override
    @Nullable
    protected Collection<CacheOperation> findCacheOperations(final Class<?> clazz) {
        return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
    }

    @Override
    @Nullable
    protected Collection<CacheOperation> findCacheOperations(final Method method) {
        return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
    }

    @Nullable
    protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
        Collection<CacheOperation> ops = null;
        for (CacheAnnotationParser annotationParser : this.annotationParsers) {
            Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
            if (annOps != null) {
                if (ops == null) {
                    ops = new ArrayList<>();
                }
                ops.addAll(annOps);
            }
        }
        return ops;
    }

    /**
     * Callback interface providing {@link CacheOperation} instance(s) based on
     * a given {@link CacheAnnotationParser}.
     */
    @FunctionalInterface
    protected interface CacheOperationProvider {

        /**
         * Return the {@link CacheOperation} instance(s) provided by the specified parser.
         * @param parser the parser to use
         * @return the cache operations, or {@code null} if none found
         */
        @Nullable
        Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser);
    }
View Code

  具体实现使用回调模式,用Set<CacheAnnotaionParser>中的每一个CacheAnnotaionParser去解析一个方法或类,然后将得到的List<CacheOperation>合并,最终返回。

    其中determineCacheOperations使用的CacheAnnotaionParser,该接口定义了两个方法:

Collection<CacheOperation> parseCacheAnnotations(Class<?> type);    //解析类上的标注,并相应创建CacheOperation
Collection<CacheOperation> parseCacheAnnotations(Method method);    //解析方法上的标注,并相应创建CacheOperation  

  其默认实现类为SpringCacheAnnotationParser,在其内部对SpringCache的几个标注@Cacheable、@CachePut、@CacheEvict、@Caching进行了解析,并相应的创建CacheableOperation、CacheEvictOperation、CachePutOperation,核心方法如下:

    private Collection<CacheOperation> parseCacheAnnotations(
            DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {

        Collection<CacheOperation> ops = null;

        Collection<Cacheable> cacheables = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class) :
                AnnotatedElementUtils.findAllMergedAnnotations(ae, Cacheable.class));
        if (!cacheables.isEmpty()) {
            ops = lazyInit(null);
            for (Cacheable cacheable : cacheables) {
                ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
            }
        }
        Collection<CacheEvict> evicts = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class) :
                AnnotatedElementUtils.findAllMergedAnnotations(ae, CacheEvict.class));
        if (!evicts.isEmpty()) {
            ops = lazyInit(ops);
            for (CacheEvict evict : evicts) {
                ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
            }
        }
        Collection<CachePut> puts = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class) :
                AnnotatedElementUtils.findAllMergedAnnotations(ae, CachePut.class));
        if (!puts.isEmpty()) {
            ops = lazyInit(ops);
            for (CachePut put : puts) {
                ops.add(parsePutAnnotation(ae, cachingConfig, put));
            }
        }
        Collection<Caching> cachings = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class) :
                AnnotatedElementUtils.findAllMergedAnnotations(ae, Caching.class));
        if (!cachings.isEmpty()) {
            ops = lazyInit(ops);
            for (Caching caching : cachings) {
                Collection<CacheOperation> cachingOps = parseCachingAnnotation(ae, cachingConfig, caching);
                if (cachingOps != null) {
                    ops.addAll(cachingOps);
                }
            }
        }

        return ops;
    }
View Code

  参数中AnnotatedElement是Class和Method的父类,代表了一个可被注释的元素,该参数可以是一个Class也可以是一个Method。

  该方法业务逻辑:首先查找该类/方法上的Cacheable标注并进行合并。针对合并后的每个Cacheable创建对应的CacheableOperation;然后同样逻辑执行CacheEvict和CachePut。最后处理Caching,Caching表示的是若干组Cache标注的集合,将其解析成一组CacheOperation并添加到Collection<CacheOperation> ops中。

4.2.2、CacheInterceptor

  继承了CacheAspectSupport并实现了MethodInterceptor接口,因此它本质上是一个Advice也就是可在切面上执行的增强逻辑。CacheInterceptor切面的拦截方法代码如下:

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {

    @Override
    @Nullable
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();

        CacheOperationInvoker aopAllianceInvoker = () -> {
            try {
                return invocation.proceed();
            }
            catch (Throwable ex) {
                throw new CacheOperationInvoker.ThrowableWrapper(ex);
            }
        };

        try {
            return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
        }
        catch (CacheOperationInvoker.ThrowableWrapper th) {
            throw th.getOriginal();
        }
    }

}
View Code

  增强逻辑的核心功能是在CacheAspectSupport中实现的,其中首先调用AnnotationCacheOperationSource.getCacheOperations(method, targetClass)方法得到被调用方法的Collection<CacheOperation>,然后将这些CacheOperation以及被调用方法、调用参数、目标类、相应的Cache信息统统封装到CacheOperation上下文里,随后调用真正的核心方法:

private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts)

  该方法封装了SpringCache核心的处理逻辑,也就是使用Cache配合来完成用户的方法调用,并返回结果。

4.2.3、BeanFactoryCacheOperationSourceAdvisor

  是一个上面Spring AOP介绍中的Advisor,具体来讲是一个PointcutAdvisor。它负责将CacheInterceptor与CacheOperationSourcePointcut结合起来。其内部注入了AnnotationCacheOperationSource,并创建了CacheOperationSourcePointcut:

    private CacheOperationSource cacheOperationSource;

    private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
        @Override
        @Nullable
        protected CacheOperationSource getCacheOperationSource() {
            return cacheOperationSource;
        }
    };  

  BeanFactoryCacheOperationSourceAdvisor利用Spring AOP的面向切面机制,将配置了SpringCache相关注释的类进行代理增强,并加入到其advisors处理链中。在bean创建的时候,如果需要Spring AOP代理增强,会首先取出beanfactory中所有的advisors,然后过滤出适合该Bean的advisors,加入到代理类中。其中对于PointcutAdvisor类型的advisor是通过

//逻辑抽取,非实际源码
advisor.getPointcut().getMethodMatcher().matches(method, targetClass)

  来判断该advisor是否适合用于被创建的Bean。因此最终会调用到CacheOperationSourcePointcut的matches方法,代码如下:

@Override
    public boolean matches(Method method, Class<?> targetClass) {
        CacheOperationSource cas = getCacheOperationSource();
        return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
    }

  结合上面的代码,最终会调用AnnotationCacheOperationSource.getCacheOperations(method, targetClass)方法。因此matches方法的意思就是:如果bean目标类的任何一个方法存在SpringCache相关的标注从而可以获得List<CacheOperation>,那么该bean需要由BeanFactoryCacheOperationSourceAdvisor来做切面增强,切入后执行的业务逻辑就是CacheInterceptor中的invoke(MethodInvocation invocation)方法。在该方法中,调用其父类CacheAspectSupport中的方法来完成缓存的核心处理逻辑,并返回结果。

4.3、springboot自动配置机制

  在SpringBoot中使用默认实现时,由于其自动配置机制,甚至都不需要自己配置CacheManager。在spring-boot-autoconfigure模块里有专门配置SpringCache的配置类:CacheAutoConfiguration

@Configuration
@ConditionalOnClass({CacheManager.class})
@ConditionalOnBean({CacheAspectSupport.class})
@ConditionalOnMissingBean(
    value = {CacheManager.class},
    name = {"cacheResolver"}
)
@EnableConfigurationProperties({CacheProperties.class})
@AutoConfigureBefore({HibernateJpaAutoConfiguration.class})
@AutoConfigureAfter({CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, RedisAutoConfiguration.class})
@Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class})
public class CacheAutoConfiguration {
    public CacheAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<List<CacheManagerCustomizer<?>>> customizers) {
        return new CacheManagerCustomizers((List)customizers.getIfAvailable());
    }

    @Bean
    public CacheAutoConfiguration.CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
        return new CacheAutoConfiguration.CacheManagerValidator(cacheProperties, cacheManager);
    }

    static class CacheConfigurationImportSelector implements ImportSelector {
        CacheConfigurationImportSelector() {
        }

        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            CacheType[] types = CacheType.values();
            String[] imports = new String[types.length];

            for(int i = 0; i < types.length; ++i) {
                imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
            }

            return imports;
        }
    }

    static class CacheManagerValidator implements InitializingBean {
        private final CacheProperties cacheProperties;
        private final ObjectProvider<CacheManager> cacheManager;

        CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
            this.cacheProperties = cacheProperties;
            this.cacheManager = cacheManager;
        }

        public void afterPropertiesSet() {
            Assert.notNull(this.cacheManager.getIfAvailable(), () -> {
                return "No cache manager could be auto-configured, check your configuration (caching type is '" + this.cacheProperties.getType() + "')";
            });
        }
    }

    @Configuration
    @ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class})
    @ConditionalOnBean({AbstractEntityManagerFactoryBean.class})
    protected static class CacheManagerJpaDependencyConfiguration extends EntityManagerFactoryDependsOnPostProcessor {
        public CacheManagerJpaDependencyConfiguration() {
            super(new String[]{"cacheManager"});
        }
    }
}
View Code

  是当满足这些条件:

    CacheManager.class存在;
    CacheAspectSupport对应的bean存在;
    CacheManager对应的bean不存在;
    等等......;

  时CacheAutoConfiguration配置类就生效。其中CacheAspectSupport上面已经介绍过,它是CacheInterceptor的父类,SpringCache真正的核心业务逻辑是由它实现的。当打上@CacheEnable注释时,自动配置了CacheInterceptor的bean,也就是CacheAspectSupport的bean,因此肯定存在。

  CacheAutoConfiguration通过@Import机制引入CacheManagerCustomizers.class和CacheConfigurationImportSelector.class。其中CacheConfigurationImportSelector使用CacheType.values()作为Key,遍历并创建将要加载的配置类全名的字符串数组。枚举CacheType的代码为:

public enum CacheType {

    /**
     * Generic caching using 'Cache' beans from the context.
     */
    GENERIC,

    /**
     * JCache (JSR-107) backed caching.
     */
    JCACHE,

    /**
     * EhCache backed caching.
     */
    EHCACHE,

    /**
     * Hazelcast backed caching.
     */
    HAZELCAST,

    /**
     * Infinispan backed caching.
     */
    INFINISPAN,

    /**
     * Couchbase backed caching.
     */
    COUCHBASE,

    /**
     * Redis backed caching.
     */
    REDIS,

    /**
     * Caffeine backed caching.
     */
    CAFFEINE,

    /**
     * Simple in-memory caching.
     */
    SIMPLE,

    /**
     * No caching.
     */
    NONE

}
View Code

  中预定义了11种不同的cache类型。

  同时在CacheConfigurations类中定义了各不同类型的cache对应的配置类,代码如下:

final class CacheConfigurations {

    private static final Map<CacheType, Class<?>> MAPPINGS;

    static {
        Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
        mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
        mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
        mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
        mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
        mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
        mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
        mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
        mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
        mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
        mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
        MAPPINGS = Collections.unmodifiableMap(mappings);
    }

    private CacheConfigurations() {
    }

    public static String getConfigurationClass(CacheType cacheType) {
        Class<?> configurationClass = MAPPINGS.get(cacheType);
        Assert.state(configurationClass != null, () -> "Unknown cache type " + cacheType);
        return configurationClass.getName();
    }

    public static CacheType getType(String configurationClassName) {
        for (Map.Entry<CacheType, Class<?>> entry : MAPPINGS.entrySet()) {
            if (entry.getValue().getName().equals(configurationClassName)) {
                return entry.getKey();
            }
        }
        throw new IllegalStateException(
                "Unknown configuration class " + configurationClassName);
    }

}
View Code

  这些配置类各自生效的条件并不相同:

    GenericCacheConfiguration的生效条件是存在Cache的bean但是不存在CacheManager的bean,并且没有定义spring.cache.type或者spring.cache.type的值为generic。
    SimpleCacheConfiguration的生效条件是不存在CacheManager的bean,并且没有定义spring.cache.type或者spring.cache.type的值为simple。
    NoOpCacheConfiguration的生效条件是不存在CacheManager的bean,并且没有定义spring.cache.type或者spring.cache.type的值为none。
    其他的配置类都有各自额外的要求,例如需要引入相应的类库支持。

  因此当我们没有引入任何其他类库,没有配置Cache bean并且没有指定spring.cache.type时,从上到下判断,GenericCacheConfiguration不起作用(未定义Cache bean)、后续的一系列与第三方存储实现方案集成的配置类也不起作用(未引入相应类库),最后轮到SimpleCacheConfiguration符合条件起作用了。因此此时使用SimpleCacheConfiguration来进行SpringCache的配置:

@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {

    private final CacheProperties cacheProperties;

    private final CacheManagerCustomizers customizerInvoker;

    SimpleCacheConfiguration(CacheProperties cacheProperties,
            CacheManagerCustomizers customizerInvoker) {
        this.cacheProperties = cacheProperties;
        this.customizerInvoker = customizerInvoker;
    }

    @Bean
    public ConcurrentMapCacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        List<String> cacheNames = this.cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            cacheManager.setCacheNames(cacheNames);
        }
        return this.customizerInvoker.customize(cacheManager);
    }

}
View Code

  该配置类指明使用ConcurrentMapCacheManager作为CacheManager bean,在其内部使用缓存方法的注释中指明的cache名称来创建ConcurrentMapCache类型的cache,创建cache的代码如下:

    public void setCacheNames(@Nullable Collection<String> cacheNames) {
        if (cacheNames != null) {
            for (String name : cacheNames) {
                this.cacheMap.put(name, createConcurrentMapCache(name));
            }
            this.dynamic = false;
        }
        else {
            this.dynamic = true;
        }
    }
View Code

  如上,由于SpringBoot的自动配置机制, 只需要打上@EnableCaching标注就可以启动SpringCache机制,使用其开箱即用的缓存实现方案。

4.3.1、CacheType简要说明

1、SimpleCacheManager-基于JDK ConcurrentMap的缓存

  基于JDK的Cache实现位于org.springframework.cache.concurrent包下。它允许使用ConcurrentHashMap作为后备Cache存储。

<!-- simple cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
        <set>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/>
        </set>
    </property>
</bean>

  上面的片段使用SimpleCacheManager为名为default和books的两个嵌套的ConcurrentMapCache实例创建一个CacheManager。请注意,名称是针对每个缓存直接配置的。

  由于缓存是由应用程序创建的,因此它被绑定到其生命周期,使其适用于基本用例,测试或简单应用程序。缓存很好地扩展,速度非常快,但它不提供任何管理或持久性功能,也不提供驱逐合同。

2、基于Ehcache的缓存

  注意:Ehcache 3.x完全兼容JSR-107,并且不需要专门的支持。

  对于2.x:Ehcache 2.x实现位于org.springframework.cache.ehcache包下。同样,要使用它,只需要声明适当的CacheManager:

<bean id="cacheManager"
        class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>

<!-- EhCache library setup -->
<bean id="ehcache"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache.xml"/>

  这个设置引导了Spring IoC中的ehcache库(通过ehcache bean),然后将它连接到专用的CacheManager实现中。请注意,整个ehcache特定配置是从ehcache.xml中读取的。

3、Caffeine缓存

  Caffeine是对Guava缓存的Java 8重写,其实现位于org.springframework.cache.caffeine包下,并提供对咖啡因的几个功能的访问。根据需要配置创建缓存的CacheManager非常简单:

<bean id="cacheManager"
        class="org.springframework.cache.caffeine.CaffeineCacheManager"/>

也可以提供明确使用的缓存。在那种情况下,只有那些经理可以获得这些信息:

<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
    <property name="caches">
        <set>
            <value>default</value>
            <value>books</value>
        </set>
    </property>
</bean>

4、基于GemFire的缓存

  GemFire是一款面向内存/磁盘备份,可弹性扩展,持续可用,活动(具有内置基于模式的订阅通知),全球复制的数据库并提供全功能的边缘缓存。有关如何将GemFire用作CacheManager(以及更多)的更多信息,请参阅Spring Data GemFire参考文档。

5、JSR-107 Cache

  Spring的缓存抽象也可以使用兼容JSR-107的缓存。 JCache实现位于org.springframework.cache.jcache包下。

<bean id="cacheManager"
        class="org.springframework.cache.jcache.JCacheCacheManager"
        p:cache-manager-ref="jCacheManager"/>

<!-- JSR-107 cache manager setup  -->
<bean id="jCacheManager" .../>

6、处理没有后台存储的缓存

  有时在切换环境或进行测试时,可能会有缓存声明而没有配置实际的后备缓存。由于这是一个无效的配置,因此在运行时将会抛出异常,因为高速缓存基础结构无法找到合适的存储。在这种情况下,而不是删除缓存声明(这可能很乏味),可以在一个简单的虚拟缓存中进行连接,该缓存不执行缓存 - 也就是强制每次都执行缓存的方法:

<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers">
        <list>
            <ref bean="jdkCache"/>
            <ref bean="gemfireCache"/>
        </list>
    </property>
    <property name="fallbackToNoOpCache" value="true"/>
</bean>

  上面的CompositeCacheManager链接多个CacheManagers,另外还通过fallbackToNoOpCache标志添加了一个无操作缓存,该缓存对于未由所配置的缓存管理器处理的所有定义。也就是说,在jdkCache或gemfireCache(以上配置)中找不到的每个缓存定义都将由no op缓存处理,该缓存不会存储任何导致每次都要执行的目标方法的信息。

7、插入不同的后端缓存

显然有很多缓存产品可以用作后备存储。为了插入它们,需要提供CacheManager和Cache实现,因为不幸的是,我们没有可用的标准。这听起来可能听起来很难,因为在实践中,这些类往往是简单的适配器,可以将缓存抽象框架映射到存储API的顶层,就像ehcache类可以显示的一样。大多数CacheManager类可以使用org.springframework.cache.support包中的类,例如AbstractCacheManager,它负责处理boiler-plate代码,只留下实际的映射。

8、如何设置TTL / TTI /驱逐策略/ XXX功能?

  直接通过您的缓存提供商。抽象不是缓存实现。您正在使用的解决方案可能支持各种数据策略和其他解决方案不支持的不同拓扑(例如,使用JDK ConcurrentHashMap)暴露在缓存抽象中是无用的,因为没有后援支持。这些功能应通过支持缓存直接控制,配置时或通过其本地API。

 

posted @ 2018-06-11 17:18  bjlhx15  阅读(2744)  评论(0编辑  收藏  举报
Copyright ©2011~2020 JD-李宏旭