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;
}
可以看到有三个属性:
- mode:指定AOP的模式,当值为AdviceMode.PROXY时表示使用Spring aop,当值为当值为AdviceMode.ASPECTJ时,表示使用AspectJ。
- 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属性时候才允许放入缓存。