springboot中带有与缓存相关的注解分别是:
1.Cacheable
对于Cacheable注解的方法,执行该方法时首先从相应的缓存中寻找数据,如果找到则将缓存中的数据作为方法的执行结果返回;如果没有找到,则执行该方法,并将执行结果放入缓存。
2.CachePut
对于CachePut注解的方法,执行该方法时会将方法的执行结果更新入缓存。
3.CacheEvict
对于CacheEvict注解的方法,执行该方法时会删除对应的缓存。
4.Caching
包含了上述3个注解。
要在springboot中使用上述的缓存注解,需要在项目中加上@EnableCaching注解。EnableCaching中通过@import注解引入CachingConfigurationSelector类,将AutoProxyRegistrar类和ProxyCachingConfiguration注入IOC容器。
AutoProxyRegistrar是自动代理生成器,与EnableProxyTargetClass的proxyTargetClass和AdviceMode相关,AdviceMode为PROXY则需要注册APC,proxyTargetClass为true则必须使用CGLIB代理。EnableCaching暴露了代理的属性,因此这个类主要是为了确定属性正确。
ProxyCachingConfiguration是缓存相关的配置类,使我们分析的重点,ProxyCachingConfiguration注入了3个bean类BeanFactoryCacheOperationSourceAdvisor、CacheOperationSource和CacheInterceptor。CacheOperationSource类在CacheInterceptor注入时进行了使用,其它地方并没有什么作用。
首先看CacheInterceptor方面,该方法是缓存注解的拦截器,也就是我们通常说的AOP中的通知,根据AbstractCachingConfiguration的setConfigurers方法我们能看出,我们可以通过一个注入IOC容器的实现CachingConfigurer接口的类来指定CacheInterceptor的一些属性,作为后续缓存操作的默认属性,CacheResolver和CacheManager都对应CacheInterceptor的CacheResolver属性,用来指定使用的缓存工具,keyGenerator用来指定生成缓存key的keyGenerator类,errorHandler用来指定出错时的异常处理。事实上,大多数springboot中AOP注解的通过都是通过这种方式来实现advice的元素的配置化的。
@Autowired(required = false)
void setConfigurers(Collection<CachingConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException(configurers.size() + " implementations of " +
"CachingConfigurer were found when only 1 was expected. " +
"Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all.");
}
CachingConfigurer configurer = configurers.iterator().next();
useCachingConfigurer(configurer);
}
然后看BeanFactoryCacheOperationSourceAdvisor,这是一个PointcutAdvisor类,包含了advice元素和pointcut元素,将advice注入pointcut。BeanFactoryCacheOperationSourceAdvisor类中的advice即为CacheInterceptor,接下来我们看pointcut的值。
private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
@Override
@Nullable
protected CacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
};
进入抽象类CacheOperationSourcePointcut查看其中关键的matches方法,该方法返回为true的连接点就是我们的pointcut,即cas.getCacheOperations(method, targetClass)返回的集合不为空。
@Override
public boolean matches(Method method, @Nullable Class<?> targetClass) {
CacheOperationSource cas = getCacheOperationSource();
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
else {
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isDebugEnabled()) {
logger.debug("Adding cacheable method '" + method.getName() + "'