Spring Cache源码分析

 2019-11-02 21:05  阅读(649)
文章分类:Spring boot

Spring Cache源码分析

2018-05-05 10:55:20 星期六

1. @EnableCaching

我们通过在配置类中使用@EnableCaching开启Spring的缓存功能。

@EnableCaching 源码如下:

@Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(CachingConfigurationSelector.class)
    public @interface EnableCaching {

        boolean proxyTargetClass() default false;

        AdviceMode mode() default AdviceMode.PROXY;

        int order() default Ordered.LOWEST_PRECEDENCE;

    }

可以看到有三个属性:

  1. mode:指定AOP的模式,当值为AdviceMode.PROXY时表示使用Spring aop,当值为当值为AdviceMode.ASPECTJ时,表示使用AspectJ。
  2. proxyTargetClass:属性值为false时,表示使用jdk代理,为true时则表示使用cglib代理。

@EnableCaching通过@Import注解引入CachingConfigurationSelector类,该类的主要代码如下:

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {

        //...

        @Override
        public String[] selectImports(AdviceMode adviceMode) {
            switch (adviceMode) {
                case PROXY:
                    return getProxyImports();
                case ASPECTJ:
                    return getAspectJImports();
                default:
                    return null;
            }
        }

        private String[] getProxyImports() {
            List<String> result = new ArrayList<String>();
            result.add(AutoProxyRegistrar.class.getName());
            result.add(ProxyCachingConfiguration.class.getName());
            if (jsr107Present && jcacheImplPresent) {
                result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
            }
            return result.toArray(new String[result.size()]);
        }

        //...
    }

由源码可看出,CachingConfigurationSelector将AutoProxyRegistrar和ProxyCachingConfiguration注入到IOC容器中。

2. AutoProxyRegistrar

我们知道要想使用Spring aop,除了要有被代理类(target)和切面(advisor:advice+pointcut)外,还需要创建代理对象,而AutoProxyCreator就可以自动为我们创建代理对象,具体参考:自动创建代理 AutoProxyCreator

AutoProxyRegistrar的部分代码如下:

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

            //...
            if (mode == AdviceMode.PROXY) {
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                if ((Boolean) proxyTargetClass) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                    return;
                }
            }
            //...
        }

    }

其内部调用到了AopConfigUtils.registerAutoProxyCreatorIfNecessary()方法,其作用是在IOC容器中注册一个AutoProxyCreator。

AopConfigUtils的部分代码如下:

public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        return registerAutoProxyCreatorIfNecessary(registry, null);
    }

    public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
    }

由上述代码可知,最终被注入到IOC容器中的AutoProxyCreator是InfrastructureAdvisorAutoProxyCreator。InfrastructureAdvisorAutoProxyCreator的特殊性在于,它只会为基础设施类型的Advisor自动创建代理对象,源码如下:

public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {

        private ConfigurableListableBeanFactory beanFactory;

        @Override
        protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            super.initBeanFactory(beanFactory);
            this.beanFactory = beanFactory;
        }

        @Override
        protected boolean isEligibleAdvisorBean(String beanName) {
            return (this.beanFactory.containsBeanDefinition(beanName) &&
                    this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
        }

    }

其中isEligibleAdvisorBean决定了会为哪些Advisor自动创建代理对象。由代码可知,只有role为BeanDefinition.ROLE_INFRASTRUCTURE的满足条件。

3. ProxyCachingConfiguration

ProxyCachingConfiguration才是重点,源码如下:

@Configuration
    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());
            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;
        }

    }

ProxyCachingConfiguration是一个配置类,它为IOC容器中注入了3个bean。最重要的是BeanFactoryCacheOperationSourceAdvisor,可以看到它是一个Advisor。看到这就明白了,因为容器中已经存在了一个AopPorxyCreator,现在又将Advisor(Advice+Pointcut)注入到IOC容器中,Spring Cache所依赖的AOP就可以开始工作了。

3.1 AnnotationCacheOperationSource

AnnotationCacheOperationSource实现了CacheOperationSource接口,该接口中只有一个方法:

public interface CacheOperationSource {

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

    }

CacheOperation是对缓存操作的抽象,其实现类有CacheEvictOperation、CachePutOperation和CacheableOperation等。

CacheOperationSource的作用是获得指定方法上的所有缓存操作,AnnotationCacheOperationSource最终是委托CacheAnnotationParser处理:

protected Collection<CacheOperation> findCacheOperations(final Method method) {
        return determineCacheOperations(new CacheOperationProvider() {
            @Override
                public Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser) {
                return parser.parseCacheAnnotations(method);
            }
        });
    }

CacheAnnotationParser的parseCacheAnnotations方法如下:

protected Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
        Collection<CacheOperation> ops = null;

        Collection<Cacheable> cacheables = AnnotatedElementUtils.findAllMergedAnnotations(ae, Cacheable.class);
        if (!cacheables.isEmpty()) {
            ops = lazyInit(ops);
            for (Cacheable cacheable : cacheables) {
                ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
            }
        }
        Collection<CacheEvict> evicts = AnnotatedElementUtils.findAllMergedAnnotations(ae, CacheEvict.class);
        if (!evicts.isEmpty()) {
            ops = lazyInit(ops);
            for (CacheEvict evict : evicts) {
                ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
            }
        }
        Collection<CachePut> puts = AnnotatedElementUtils.findAllMergedAnnotations(ae, CachePut.class);
        if (!puts.isEmpty()) {
            ops = lazyInit(ops);
            for (CachePut put : puts) {
                ops.add(parsePutAnnotation(ae, cachingConfig, put));
            }
        }
        Collection<Caching> cachings = 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;
    }

3.2 BeanFactoryCacheOperationSourceAdvisor

ProxyCachingConfiguration使用以下方式配置Advisor:

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

cacheAdvisor()方法上使用@Role指定了返回值BeanFactoryCacheOperationSourceAdvisor的role属性值为BeanDefinition.ROLE_INFRASTRUCTURE,这也是InfrastructureAdvisorAutoProxyCreator能为BeanFactoryCacheOperationSourceAdvisor自动创建代理的原因。

BeanFactoryCacheOperationSourceAdvisor注入了CacheInterceptor作为增强方法。

BeanFactoryCacheOperationSourceAdvisor源码如下:

public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

        private CacheOperationSource cacheOperationSource;

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

        public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
            this.cacheOperationSource = cacheOperationSource;
        }

        public void setClassFilter(ClassFilter classFilter) {
            this.pointcut.setClassFilter(classFilter);
        }

        @Override
        public Pointcut getPointcut() {
            return this.pointcut;
        }

    }

Advisor由Advice和Pointcut组成。Advice在ProxyCachingConfiguration中注入,而Pointcut则使用CacheOperationSourcePointcut。

CacheOperationSourcePointcut源码如下:

abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

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

        protected abstract CacheOperationSource getCacheOperationSource();

    }

由源码可知,只有标注了@Cacheable、@CachePut或@CacheEvict注解的方法才会被匹配到。

3.3 CacheInterceptor

在ProxyCachingConfiguration中使用以下方式配置CacheInterceptor:

@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;
    }

其类定义如下:

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
        @Override
        public Object invoke(final MethodInvocation invocation) throws Throwable {
            Method method = invocation.getMethod();

            CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
                @Override
                public Object invoke() {
                    try {
                        return invocation.proceed();
                    }
                    catch (Throwable ex) {
                        throw new ThrowableWrapper(ex);
                    }
                }
            };

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

    }

由此可见CacheInterceptor是环绕增强。其主要的方法实现在CacheAspectSupport中。

CacheAspectSupport中的实现如下,首先是execute方法:

protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
        // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
        if (this.initialized) {
            Class<?> targetClass = getTargetClass(target);
            Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
            if (!CollectionUtils.isEmpty(operations)) {
                return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass));
            }
        }

        return invoker.invoke();
    }

该方法将目标方法,目标方法的参数,目标类,目标方法上使用的缓存操作等封装到CacheOperationContexts中,并向下传递。CacheOperationContexts内保存了一个MultiValueMap,源码如下:

private class CacheOperationContexts {

        private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts =
            new LinkedMultiValueMap<Class<? extends CacheOperation>, CacheOperationContext>();

        private final boolean sync;

        public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
                                      Object[] args, Object target, Class<?> targetClass) {

            for (CacheOperation operation : operations) {
                this.contexts.add(operation.getClass(), getOperationContext(operation, method, args, target, targetClass));
            }
            this.sync = determineSyncFlag(method);
        }

        public Collection<CacheOperationContext> get(Class<? extends CacheOperation> operationClass) {
            Collection<CacheOperationContext> result = this.contexts.get(operationClass);
            return (result != null ? result : Collections.<CacheOperationContext>emptyList());
        }
        //...
    }

当调用LinkedMultiValueMap的add方法时候,相同key的value会被保存到一个List中。每个CacheOperationContext会封装一个CacheOperation.当调用get方法的时候,则会返回对应的CacheOperationContext列表。

invoke(final MethodInvocation invocation)方法还向下调用了execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts)方法。源码如下:

private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
        // Special handling of synchronized invocation
        if (contexts.isSynchronized()) {
            CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
            if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
                Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
                Cache cache = context.getCaches().iterator().next();
                try {
                    return cache.get(key, new Callable<Object>() {
                        @Override
                            public Object call() throws Exception {
                            return invokeOperation(invoker);
                        }
                    });
                }
                catch (Cache.ValueRetrievalException ex) {
                    // The invoker wraps any Throwable in a ThrowableWrapper instance so we
                    // can just make sure that one bubbles up the stack.
                    throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
                }
            }
            else {
                // No caching required, only call the underlying method
                return invokeOperation(invoker);
            }
        }

        // Process any early evictions
        processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
                           CacheOperationExpressionEvaluator.NO_RESULT);

        // Check if we have a cached item matching the conditions
        Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

        // Collect puts from any @Cacheable miss, if no cached item is found
        List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>();
        if (cacheHit == null) {
            collectPutRequests(contexts.get(CacheableOperation.class),
                               CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
        }

        Object cacheValue;
        Object returnValue;

        if (cacheHit != null && cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
            // If there are no put requests, just use the cache hit
            cacheValue = cacheHit.get();
            if (method.getReturnType() == javaUtilOptionalClass &&
                (cacheValue == null || cacheValue.getClass() != javaUtilOptionalClass)) {
                returnValue = OptionalUnwrapper.wrap(cacheValue);
            }
            else {
                returnValue = cacheValue;
            }
        }
        else {
            // Invoke the method if we don't have a cache hit
            returnValue = invokeOperation(invoker);
            if (returnValue != null && returnValue.getClass() == javaUtilOptionalClass) {
                cacheValue = OptionalUnwrapper.unwrap(returnValue);
            }
            else {
                cacheValue = returnValue;
            }
        }

        // Collect any explicit @CachePuts
        collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

        // Process any collected put requests, either from @CachePut or a @Cacheable miss
        for (CachePutRequest cachePutRequest : cachePutRequests) {
            cachePutRequest.apply(cacheValue);
        }

        // Process any late evictions
        processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

        return returnValue;
    }

1. if (contexts.isSynchronized()){}代码块

因为只有@Cacheable注解有sync属性,因此这一部分代码用于处理@Cacheable的相关操作。

// 1. 执行@Cacheable注解对应的操作:只有@Cacheable注解有sync属性,当sync为true时,contexts.isSynchronized()返回true,执行以下方法
    if (contexts.isSynchronized()) {
        CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next(); //一个方法上只允许标注一个@Cacheable
        if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) { //如果@Cacheable的condition属性为空字符串,或者满足condition条件则执行以下方法
            Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
            Cache cache = context.getCaches().iterator().next();
            try {
                return cache.get(key, new Callable<Object>() {  //执行底层Cache的get方法,当Cache中得不到的会先执行目标方法,然后将结果保存到Cache中
                    @Override
                        public Object call() throws Exception {
                        return invokeOperation(invoker); //执行目标方法
                    }
                });
            }
            catch (Cache.ValueRetrievalException ex) {
                // The invoker wraps any Throwable in a ThrowableWrapper instance so we
                // can just make sure that one bubbles up the stack.
                throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
            }
        }
        else {
            // No caching required, only call the underlying method
            return invokeOperation(invoker);
        }
    }

在代码中用到了底层缓存的get方法,以ConcurrentMapCache为例:ConcurrentMapCache的get方法如下:

public <T> T get(Object key, Callable<T> valueLoader) {
        if (this.store.containsKey(key)) {  //1. 尝试从缓存中获得key对应的value,存在则返回
            return (T) get(key).get();
        }
        else {
            synchronized (this.store) {
                if (this.store.containsKey(key)) { //再次尝试获得缓存纪录
                    return (T) get(key).get();
                }
                T value;
                try {
                    value = valueLoader.call(); //2. 如果获取不到,则执行目标方法
                }
                catch (Exception ex) {
                    throw new ValueRetrievalException(key, valueLoader, ex);
                }
                put(key, value); //3. 将目标方法的返回值放入缓存中
                return value;//4. 返回
            }
        }
    }

2. processCacheEvicts方法

@CacheEvict注解有一个beforeInvocation属性,当属性值为true时,表示缓存在目标方法执行前就清除掉。当为false时,则表示缓存要在目标方法执行完成后才清除。

因此在execute方法中两次调用了processCacheEvicts方法方法:

// Process any early evictions
    processCacheEvicts(contexts.get(CacheEvictOperation.class), true,CacheOperationExpressionEvaluator.NO_RESULT);

    // Process any late evictions
    processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

前一次调用只有beforeInvocation为true时才执行清除缓存的操作,第二次调用只有beforeInvocation为false时才执行清除缓存的操作,processCacheEvicts方法如下:

private void processCacheEvicts(Collection<CacheOperationContext> contexts, boolean beforeInvocation, Object result) {
        for (CacheOperationContext context : contexts) {
            CacheEvictOperation operation = (CacheEvictOperation) context.metadata.operation;
            if (beforeInvocation == operation.isBeforeInvocation() && isConditionPassing(context, result)) {
                performCacheEvict(context, operation, result);
            }
        }
    }

    private void performCacheEvict(CacheOperationContext context, CacheEvictOperation operation, Object result) {
        Object key = null;
        for (Cache cache : context.getCaches()) {
            if (operation.isCacheWide()) {
                logInvalidating(context, operation, null);
                doClear(cache);
            }
            else {
                if (key == null) {
                    key = context.generateKey(result);
                }
                logInvalidating(context, operation, key);
                doEvict(cache, key);
            }
        }
    }

CacheEvictOperation的cacheWide属性对应于@CacheEvict注解的allEntries,org.springframework.cache.annotation.SpringCacheAnnotationParser#parseEvictAnnotation方法如下:

CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) {
        CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder();

        builder.setName(ae.toString());
        builder.setCacheNames(cacheEvict.cacheNames());
        builder.setCondition(cacheEvict.condition());
        builder.setKey(cacheEvict.key());
        builder.setKeyGenerator(cacheEvict.keyGenerator());
        builder.setCacheManager(cacheEvict.cacheManager());
        builder.setCacheResolver(cacheEvict.cacheResolver());
        builder.setCacheWide(cacheEvict.allEntries());
        builder.setBeforeInvocation(cacheEvict.beforeInvocation());

        defaultConfig.applyDefault(builder);
        CacheEvictOperation op = builder.build();
        validateCacheOperation(ae, op);

        return op;
    }

当operation.isCacheWide()为true时将执行doClear方法,清空缓存中的所有内容。为false是,则执行doEvict方法,清除指定的纪录。

3. @Cacheable的解析

//1. 尝试从缓存中获得key对应的值
    Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

    // 2. 如果没有获取到就将CacheOperationContext封装到CachePutRequest中,并保存到cachePutRequests集合内
    List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>();
    if (cacheHit == null) {
        collectPutRequests(contexts.get(CacheableOperation.class),
                           CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
    }

    Object cacheValue;
    Object returnValue;

    //2. 如果获得到了对应的值就将结果保存到returnValue属性值中
    if (cacheHit != null && cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
        // If there are no put requests, just use the cache hit
        cacheValue = cacheHit.get();
        if (method.getReturnType() == javaUtilOptionalClass &&
            (cacheValue == null || cacheValue.getClass() != javaUtilOptionalClass)) {
            returnValue = OptionalUnwrapper.wrap(cacheValue);
        }
        else {
            returnValue = cacheValue;
        }
    }
    else {
        //3. 如果获取不到就执行目标方法
        returnValue = invokeOperation(invoker);
        if (returnValue != null && returnValue.getClass() == javaUtilOptionalClass) {
            cacheValue = OptionalUnwrapper.unwrap(returnValue);
        }
        else {
            cacheValue = returnValue;
        }
    }

如果当前方法上标注的不是@Cacheable注解或者@Cacheable的sync属性不为true,那么就会执行上述代码。

对于@Cacheable注解而言,首先执行findCachedItem方法,尝试从缓存中获得key对应的值,如果获取到了,就把结果保存到returnValue属性中;如果获取不到,则运行目标方法,结果保存到returnValue属性中,并将对应的CacheOperationContext封装为CachePutRequest,保存到cachePutRequests集合里(调用collectPutRequests方法)。

对于@CachePut,因为cachehit属性值恒为null,因此会运行目标方法,并将结果保存到returnValue变量中:

returnValue = invokeOperation(invoker);

4. collectPutRequests

execute方法的最后才解析@CachePut注解:

// Collect any explicit @CachePuts
    collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

主要使用到了collectPutRequests方法:

private void collectPutRequests(Collection<CacheOperationContext> contexts,Object result, Collection<CachePutRequest> putRequests) {

        for (CacheOperationContext context : contexts) {
            if (isConditionPassing(context, result)) {
                Object key = generateKey(context, result);
                putRequests.add(new CachePutRequest(context, key));
            }
        }
    }

可以看到collectPutRequests的作用就是将每个代表@CachePut的CacheOperationContext封装为CachePutRequest,然后保存到cachePutRequests集合内。

现在cachePutRequests集合除了有缓存中取不到值的@Cacheable(对应的ContextOperationContext)外,还有@CachePut(对应的ContextOperationContext)。

5. 写入缓存

execute的最后一步是逐个运行集合中的CachePutRequest:

for (CachePutRequest cachePutRequest : cachePutRequests) {
        cachePutRequest.apply(cacheValue);
    }

cacheValue可以简单的认为是returnValue的别名。

我们来看一下CachePutRequest的源码:

private class CachePutRequest {

        private final CacheOperationContext context;

        private final Object key;

        public CachePutRequest(CacheOperationContext context, Object key) {
            this.context = context;
            this.key = key;
        }

        public void apply(Object result) {
            if (this.context.canPutToCache(result)) {
                for (Cache cache : this.context.getCaches()) {
                    doPut(cache, this.key, result);
                }
            }
        }
    }

CacheOperationContext的canPutToCache方法如下:

protected boolean canPutToCache(Object value) {
        String unless = "";
        if (this.metadata.operation instanceof CacheableOperation) {
            unless = ((CacheableOperation) this.metadata.operation).getUnless();
        }
        else if (this.metadata.operation instanceof CachePutOperation) {
            unless = ((CachePutOperation) this.metadata.operation).getUnless();
        }
        if (StringUtils.hasText(unless)) {
            EvaluationContext evaluationContext = createEvaluationContext(value);
            return !evaluator.unless(unless, this.methodCacheKey, evaluationContext);
        }
        return true;
    }

只有@Cacheable以及@CachePut标注的方法的执行结果才有可能存入缓存中。如果这两个注解没有指定unless属性,那么执行结果将被放入缓存中,否则只有当结果满足unless属性时候才允许放入缓存。


来源:http://ddrv.cn

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring Cache源码分析

相关推荐