Spring Cache (1) - 源码分析

最近想写批量操作的缓存 AOP,于是研究了一下 Spring 原生的 Cache 源码,以便编些自己的 BatchCache AOP。

Spring Cache

配置(Configuration)

基类:AbstractCachingConfiguration

包含几个关键配置对象可供配置:

1
2
3
4
5
protected AnnotationAttributes enableCaching;
protected CacheManager cacheManager;
protected CacheResolver cacheResolver;
protected KeyGenerator keyGenerator;
protected CacheErrorHandler errorHandler;

入口配置:ProxyCachingConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@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;
}

注解解析器(Annotation Parser)

CacheAnnotationParser 接口

包含两个方法的声明。

1
2
3
4
public interface CacheAnnotationParser {
Collection<CacheOperation> parseCacheAnnotations(Class<?> type);
Collection<CacheOperation> parseCacheAnnotations(Method method);
}

能够将 class 或 method 的 cache 注解解析为 CacheOperation 操作。

SpringCacheAnnotationParser 类

SpringCacheAnnotationParser 实现了 CacheAnnotationParser。同时还实现了 Serializable

两个 parseCacheAnnotations 方法的内部实现,都是首先通过 getDefaultCacheConfig(type)(对于 method,参数取 method.getDeclaringClass())取一个默认的 cache 配置,然后走同一套 parseCacheAnnotations(defaultConfig, type) 逻辑。

基本上调用方只有 AnnotationCacheOperationSource

parseCacheAnnotations

通过 getAllMergedAnnotations 方法,将类型(Class<?>Method)的 CacheableCacheEvictCachePutCaching 注解分别取出,分别通过 parseCacheableAnnotationparseEvictAnnotationparsePutAnnotationparseCachingAnnotation方法解析为成 CacheOperation,最终合并为一个集合。

前三个 parseXXXAnnotation 的方法基本类似,取出注解中的参数(name、cacheName、key 等等,前三个注解略有不同),与 defaultConfig 合并,校验参数并返回。parseCachingAnnotation 是前三个注解的组合注解,所以将其内部的三种分别取出,再执行前三个 parseXXXAnnotation 方法。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
CacheableOperation.Builder builder = new CacheableOperation.Builder();

builder.setName(ae.toString());
builder.setCacheNames(cacheable.cacheNames());
builder.setCondition(cacheable.condition());
builder.setUnless(cacheable.unless());
builder.setKey(cacheable.key());
builder.setKeyGenerator(cacheable.keyGenerator());
builder.setCacheManager(cacheable.cacheManager());
builder.setCacheResolver(cacheable.cacheResolver());
builder.setSync(cacheable.sync());

defaultConfig.applyDefault(builder);
CacheableOperation op = builder.build();
validateCacheOperation(ae, op);

return op;
}

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;
}

CacheOperation parsePutAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) {
CachePutOperation.Builder builder = new CachePutOperation.Builder();

builder.setName(ae.toString());
builder.setCacheNames(cachePut.cacheNames());
builder.setCondition(cachePut.condition());
builder.setUnless(cachePut.unless());
builder.setKey(cachePut.key());
builder.setKeyGenerator(cachePut.keyGenerator());
builder.setCacheManager(cachePut.cacheManager());
builder.setCacheResolver(cachePut.cacheResolver());

defaultConfig.applyDefault(builder);
CachePutOperation op = builder.build();
validateCacheOperation(ae, op);

return op;
}

Collection<CacheOperation> parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching) {
Collection<CacheOperation> ops = null;

Cacheable[] cacheables = caching.cacheable();
if (!ObjectUtils.isEmpty(cacheables)) {
ops = lazyInit(ops);
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
}
}
CacheEvict[] cacheEvicts = caching.evict();
if (!ObjectUtils.isEmpty(cacheEvicts)) {
ops = lazyInit(ops);
for (CacheEvict cacheEvict : cacheEvicts) {
ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
}
}
CachePut[] cachePuts = caching.put();
if (!ObjectUtils.isEmpty(cachePuts)) {
ops = lazyInit(ops);
for (CachePut cachePut : cachePuts) {
ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
}
}

return ops;
}

最终校验 CacheableOperation 时,key 和 keyGenerator 至少需要有一个定义,cacheManager 和 cacheResolver 至少需要有一个定义。

缓存操作源

CacheOperationSource 接口

只定义了一个方法,用于获取 CacheOperation 集合

1
Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass);

AbstractFallbackCacheOperationSource

做了一层缓存。

computeCacheOperations(Method method, Class<?> targetClass) 计算 CacheOperation 集合。

取 class 的 CacheConfig 注解作为 DefaultCacheConfig,没有则全为 null。

也就是说,Method 的 CacheConfig 定义需要在声明它的 class 上。

computeCacheOperations 方法

方法声明为:

1
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass)

首先,如果需要的话,过滤只考虑 public 方法。(默认需要过滤)

其次,通过 ClassUtils.getMostSpecificMethodBridgeMethodResolver.findBridgedMethod 找到最合适的 method 定义。

桥接方法是为了兼容 Java 1.5 没有泛型语义时允许传递 Object 的问题而由编译器自动生成的方法。

先使用 findCacheOperations(Class<?> clazz) 尝试从方法上解析 cache 操作,有则直接返回。没有则尝试使用 findCacheOperations(Method method) 从定义方法的类上解析,有则直接返回。

都取不到则尝试从原方法上用相同的流程解析。

都解析不到则返回 null。

AnnotationCacheOperationSource 类

AnnotationCacheOperationSource 继承了 AbstractFallbackCacheOperationSource 并实现了 Serializable

AnnotationCacheOperationSource 会读取 Spring 的 CacheableCachePutCacheEvict 批注,并将相应的 cache operation 定义公开给 Spring 的缓存基础结构。此类也可以用作自定义 CacheOperationSource 的基类。

内部定义了一个 Set<CacheAnnotationParser> annotationParsers 保存用到的注解分析器。

提供了自定义是否只使用 public 方法和自定义缓存注解解析器(CacheAnnotationParser)的构造方法。默认只解析 public 方法,且解析器集合只有上文提到的 SpringCacheAnnotationParser

determineCacheOperations(provider) 确定给定 CacheOperationProviderCacheOperation。该实现委托配置的 CacheAnnotationParser(默认为 SpringCacheAnnotationParser)来将已知的注解解析为 Spring 的元 attribute 类。可以重写以支持带有 caching metadata 的自定义注解。参数 provider 是要使用的 cache operation 提供者,类型为 CacheOperationProvider

定义了基类使用的 findCacheOperations 方法,实现是遍历所有 annotationParsers,调用其 parseCacheAnnotations,然后将 cache 操作合并为一个 set。

CacheOperationSource 用法小结

这个类,最终对外提供的是定义在 AbstractFallbackCacheOperationSource 下的 getCacheOperations(Method method, Class<?> targetClass) 方法。用于 CacheAspectSupportCacheOperationSourcePointcut

Pointcut

与 Cache 无关的 AOP 基类

StaticMethodMatcherPointcut -> Pointcut
StaticMethodMatcherPointcut -> StaticMethodMatcher -> MethodMatcher

CacheOperationSourcePointcut

是一个抽象类,继承自 StaticMethodMatcherPointcut,实现了 Serializable。

有一个待实现的 getCacheOperationSource 方法,以返回缓存操作源。

实现了基类的 match 方法。

1
2
3
4
5
@Override
public boolean matches(Method method, Class<?> targetClass) {
CacheOperationSource cas = getCacheOperationSource();
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}

也就是说,如果 cacheOperationSource 非空且 cacheOperationSource.getCacheOperations(method, targetClass) 取到了一个或多个 cache 操作则可以匹配上。

缓存操作的调用上下文 CacheOperationInvocationContext

缓存操作的调用上下文类,提供四个接口:

1
2
3
4
5
6
public interface CacheOperationInvocationContext<O extends BasicOperation> {
O getOperation();
Object getTarget();
Method getMethod();
Object[] getArgs();
}

泛型的基类定义如下:

1
2
3
public interface BasicOperation {
Set<String> getCacheNames();
}

缓存解析器(cache resolver)

CacheResolver

1
2
3
public interface CacheResolver {
Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
}

AbstractCacheResolver、SimpleCacheResolver 和 NamedCacheResolver

抽象类,实现了 CacheResolver 接口,框架提供了两个默认实现 SimpleCacheResolver 和 NamedCacheResolver。

区别在于 SimpleCacheResolver 取 Operation 上的 cacheNames,NamedCacheResolver 手动指定 cacheNames。

而后都遍历 cacheNames 执行 CacheManager.getCache(cacheName) 并整合至同一集合并返回。

CacheResolver 用法小结

对外提供了 resolveCaches 方法,主要用在 CacheAspectSupport

Interceptor 拦截器

AbstractCacheInvoker

在基本的 Cache 类的 doGet、doPut、doEvict 方法外包了一层 try-catch 并定义了异常日志的打印方式。默认直接抛出异常。

CacheAspectSupport

抽象类,CacheAspectSupport 继承自 AbstractCacheInvoker,还实现了 BeanFactoryAware, InitializingBean, SmartInitializingSingleton。

涉及到众多需要 set 的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache =
new ConcurrentHashMap<CacheOperationCacheKey, CacheOperationMetadata>(1024);

private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator();

private CacheOperationSource cacheOperationSource;

private KeyGenerator keyGenerator = new SimpleKeyGenerator();

private CacheResolver cacheResolver;

private BeanFactory beanFactory;

定义了一些对 Optional 的解包逻辑。

关键是 execute 方法。真正在方法执行前后对缓存执行对应操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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 wrapCacheValue(method, cache.get(key, new Callable<Object>() {
@Override
public Object call() throws Exception {
return unwrapReturnValue(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();
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// Invoke the method if we don't have a cache hit
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(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;
}

CacheInterceptor

继承自 CacheAspectSupport,实现了 MethodInterceptor 和 Serializable

将 CacheAspectSupport 父类的 execute 方法包装为接口的 invoke 方法的实现。

Advisor

与 Cache 无关的 AOP advisor 基类

AbstractBeanFactoryPointcutAdvisor -> AbstractPointcutAdvisor -> PointcutAdvisor -> Advisor

BeanFactoryCacheOperationSourceAdvisor

继承自 AbstractBeanFactoryPointcutAdvisor。

getPointcut 方法返回一个 CacheOperationSourcePointcut 的实现,getCacheOperationSource 返回用户自定义的 CacheOperationSource。

注册时代码如下:

1
2
3
4
5
6
7
8
9
@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;
}