Spring cache是一个缓存API层,封装了对多种缓存的通用操作,可以借助注解方便地为程序添加缓存功能。
常见的注解有@Cacheable、@CachePut、@CacheEvict,有没有想过背后的原理是什么?楼主带着疑问,阅读完Spring cache的源码后,做一个简要总结。
先说结论,核心逻辑在CacheAspectSupport类,封装了所有的缓存操作的主体逻辑,下面详细介绍。
题外话:如何阅读开源代码?
有2种方法,可以结合起来使用:
- 静态代码阅读:查找关键类、方法的usage之处,熟练使用find usages功能,找到所有相关的类、方法,静态分析核心逻辑的执行过程,一步步追根问底,直至建立全貌
- 运行时debug:在关键方法上加上断点,并且写一个单元测试调用类库/框架,熟练使用step into/step over/resume来动态分析代码的执行过程
核心类图
如图所示,可以分成以下几类class:
- Cache、CacheManager:Cache抽象了缓存的通用操作,如get、put,而CacheManager是Cache的集合,之所以需要多个Cache对象,是因为需要多种缓存失效时间、缓存条目上限等
- CacheInterceptor、CacheAspectSupport、AbstractCacheInvoker:CacheInterceptor是一个AOP方法拦截器,在方法前后做额外的逻辑,也即查询缓存、写入缓存等,它继承了CacheAspectSupport(缓存操作的主体逻辑)、AbstractCacheInvoker(封装了对Cache的读写)
- CacheOperation、AnnotationCacheOperationSource、SpringCacheAnnotationParser:CacheOperation定义了缓存操作的缓存名字、缓存key、缓存条件condition、CacheManager等,AnnotationCacheOperationSource是一个获取缓存注解对应CacheOperation的类,而SpringCacheAnnotationParser是真正解析注解的类,解析后会封装成CacheOperation集合供AnnotationCacheOperationSource查找
源码分析(带注释解释)
下面对Spring cache源码做分析,带注释解释,只摘录核心代码片段。
1、解析注解
首先看看注解是如何解析的。注解只是一个标记,要让它真正工作起来,需要对注解做解析操作,并且还要有对应的实际逻辑。
SpringCacheAnnotationParser:负责解析注解,返回CacheOperation集合
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
// 解析类级别的缓存注解
@Override
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
}
// 解析方法级别的缓存注解
@Override
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
}
// 解析缓存注解
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection<CacheOperation> ops = null;
// 解析@Cacheable注解
Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);
if (!cacheables.isEmpty()) {
ops = lazyInit(ops);
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
}
}
// 解析@CacheEvict注解
Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);
if (!evicts.isEmpty()) {
ops = lazyInit(ops);
for (CacheEvict evict : evicts) {
ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
}
}
// 解析@CachePut注解
Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);
if (!puts.isEmpty()) {
ops = lazyInit(ops);
for (CachePut put : puts) {
ops.add(parsePutAnnotation(ae, cachingConfig, put));
}
}
// 解析@Caching注解
Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class);
if (!cachings.isEmpty()) {
ops = lazyInit(ops);
for (Caching caching : cachings) {
Collection<CacheOperation> cachingOps