spring-cache源码解析
本文写作时间2024-09-29 10:41:01
前文连接
springcache参考链接:
源码解析 配合上面的视频查看
- 源码下载:
- 扩展知识
源码解析
springcache 利用 spring aop实现的缓存框架
常用的 启动注解 三个注解 以及组合注解 和 公共注解。
缓存的初始化过程
项目如果需要缓存支持,需要使用注解 @org.springframework.cache.annotation.EnableCaching
添加该注解后,会导入 CachingConfigurationSelector
类
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching
CachingConfigurationSelector
类实现了 ImportSelector
接口,根据EnableCaching.mode
,来导入相应的类,这里以默认的PROXY为例说明,会导入配置类ProxyCachingConfiguration
:
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<>(3);
result.add(AutoProxyRegistrar.class.getName());
result.add(ProxyCachingConfiguration.class.getName());
if (jsr107Present && jcacheImplPresent) {
result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
}
return StringUtils.toStringArray(result);
}
ProxyCachingConfiguration
类定义了3个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.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
interceptor.setCacheOperationSource(cacheOperationSource());
return interceptor;
}
}
同时,ProxyCachingConfiguration
的父类AbstractCachingConfiguration
,会加载自定义配置:
@Autowired(required = false)
void setConfigurers(Collection<CachingConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException(...);
}
CachingConfigurer configurer = configurers.iterator().next();
useCachingConfigurer(configurer);
}
这里可以通过 实现 CachingConfigurer
接口,来自定义CacheManager和CacheResolver等对象,
上面的Bean CacheInterceptor
对象属性初始值,就是读取这里返回的对象:
ProxyCachingConfiguration
类里面的 BeanFactoryCacheOperationSourceAdvisor
实例,继承了Advisor
接口,在获取后续的Bean时,会对这些Bean Bean.getDeclaredMethods
方法清单里的每个方法,调用matches
进行检测:
此处即是aop的织入
// BeanFactoryCacheOperationSourceAdvisor的private属性CacheOperationSourcePointcut 里的方法
@Override
public boolean matches(Method method, Class<?> targetClass) {
CacheOperationSource cas = getCacheOperationSource();
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}
这个cas就是ProxyCachingConfiguration里的AnnotationCacheOperationSource实例,通过cas.getCacheOperations方法,
调用org.springframework.cache.annotation.SpringCacheAnnotationParser类的方法parseCacheAnnotations,
读取Bean的方法上的Cacheable、CacheEvict、CachePut、Caching这4个注解定义,并转成 CacheOperation,然后缓存起来.
如果发现该Bean的方法存在上述4个注解定义时,会对该类套一层缓存代理,每次调用缓存方法,都会被 BeanFactoryCacheOperationSourceAdvisor.getAdvice()所拦截,这个getAdvice方法返回的就是上面ProxyCachingConfiguration里的CacheInterceptor实例
Bean初始化完成后,会调用 CacheInterceptor.afterSingletonsInstantiated
方法来完成初始化。
@Override
public void afterSingletonsInstantiated() {
if (getCacheResolver() == null) {
// Lazily initialize cache resolver via default cache manager...
Assert.state(this.beanFactory != null, "CacheResolver or BeanFactory must be set on cache aspect");
try {
setCacheManager(this.beanFactory.getBean(CacheManager.class));
}
catch (NoUniqueBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " +
"CacheManager found. Mark one as primary or declare a specific CacheManager to use.");
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no bean of type CacheManager found. " +
"Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.");
}
}
this.initialized = true;
}
这段代码里,设置了CacheInterceptor实例的cacheResolver属性:
- 如果有CachingConfigurer定义的CacheResolver且不为空,则使用该CacheResolver里定义的Cache;
- 如果有CachingConfigurer定义的CacheManager且不为空,则使用该CacheManager;
- 如果CacheResolver为空,则从Bean工厂去获取CacheManager的Bean,并设置为new SimpleCacheResolver(cacheManager)。
到这里,缓存的初始化加载工作基本完成了,下面是具体的缓存方法调用过程。
缓存方法执行过程:
那我们实际调用方法的时候,是怎么处理的呢?我们知道,使用了
AOP
的Bean
,会生成一个代理对象,实际调用的时候,会执行这个代理对象的一系列的Interceptor
。Spring Cache
使用的是一个叫做CacheInterceptor
的拦截器。我们如果加了缓存相应的注解,就会走到这个拦截器上。这个拦截器继承了CacheAspectSupport
类,会执行这个类的execute
方法,这个方法就是我们要分析的核心方法了。
1、调用缓存方法时,会先调用 CacheInterceptor.invoke => CacheAspectSupport.execute => AnnotationCacheOperationSource.getCacheOperations
对,就是上面缓存起来的4个Cache注解定义得到的CacheOperation.
2、然后会新建 new CacheOperationContexts
,在这个CacheOperationContexts
的构造函数里,会依次判断并获得最终要使用的CacheResolver:
CacheResolver operationCacheResolver;
if (StringUtils.hasText(operation.getCacheResolver())) {
// 使用注解的CacheResolver
operationCacheResolver = getBean(operation.getCacheResolver(), CacheResolver.class);
}
else if (StringUtils.hasText(operation.getCacheManager())) {
// 使用注解的CacheManager
CacheManager cacheManager = getBean(operation.getCacheManager(), CacheManager.class);
operationCacheResolver = new SimpleCacheResolver(cacheManager);
}
else {
// 使用CacheInterceptor实例的cacheResolver
operationCacheResolver = getCacheResolver();
Assert.state(operationCacheResolver != null, "No CacheResolver/CacheManager set");
}
得到 CacheResolver后,再调用CacheResolver.resolveCaches,得到当前方法依赖的Cache列表,其内部就是调用cacheManager.getCache,参数为:注解的cacheNames属性。
构造函数最后再收集最终要使用的cacheNames集合。
3、接着再进入CacheAspectSupport实例的另一个重载:
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts)
在这个重载方法里,依次处理缓存逻辑:
// 方法调用前的缓存过期处理,设置了CacheEvict.isBeforeInvocation属性时
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
// Cacheable注解时,去查找缓存
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
// 缓存未命中时,把Cacheable加入到CachePut的集合,后面用于写入缓存
List<CachePutRequest> cachePutRequests = new LinkedList<>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
if (cacheHit != null && !hasCachePut(contexts)) {
// If there are no put requests, just use the cache hit
cacheValue = cacheHit.get();
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// 未找到缓存时,调用目标方法
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(returnValue);
}
// 收集CachePut注解,后面写入缓存
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// 写入缓存
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}
// 处理缓存过期逻辑
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
// 返回结果给代理类
return returnValue;
4、整个源码过程如上,没有解读keyGenerator,它跟cacheResolver的过程差不多。
还有就是具体的读取、设置缓存细节也没有描述。
补充
主要涉及切面编程问题; 至于其他的cachemanage 留后查看