Spring Cache源码分析

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属性时候才允许放入缓存。

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/moranshouwang/article/details/80210469
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞