Spring Cache (4) - Operation 解析器

上一章对于 AnnotationCacheOperationSource 中用到的 SpringCacheAnnotationParser 没有细讲完,这篇单独细讲一下。

Spring Cache 的 Operation 解析器

Operation 解析器最底层的接口是 CacheAnnotationParser,我们先来看下这个接口

CacheAnnotationParser

接口的定义十分简单:

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

要求了实现类计算出 目标类 或 方法 对应的 CacheOperation 集合。

SpringCacheAnnotationParser

SpringCacheAnnotationParser 实现了 CacheAnnotationParser 这个接口:

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

从目标类或方法的声明类上取得默认的 CacheConfig,再执行 parseCacheAnnotations 方法进行解析。

我们先看这个 getDefaultCacheConfig 方法:

1
2
3
4
5
6
7
8
DefaultCacheConfig getDefaultCacheConfig(Class<?> target) {
CacheConfig annotation = AnnotatedElementUtils.getMergedAnnotation(target, CacheConfig.class);
if (annotation != null) {
return new DefaultCacheConfig(annotation.cacheNames(), annotation.keyGenerator(),
annotation.cacheManager(), annotation.cacheResolver());
}
return new DefaultCacheConfig();
}

非常简单,就是从类的 @CacheConfig 注解中取出 cacheNameskeyGeneratorcacheManagercacheResolver 这四个属性值。

parseCacheAnnotations 方法就较为复杂了,需要解析 CacheableCacheEvictCachePutCaching 四种注解:

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
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection<CacheOperation> ops = null;

Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);
if (!cacheables.isEmpty()) {
ops = lazyInit(ops);
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
}
}

Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);
if (!evicts.isEmpty()) {
ops = lazyInit(ops);
for (CacheEvict evict : evicts) {
ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
}
}

Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);
if (!puts.isEmpty()) {
ops = lazyInit(ops);
for (CachePut put : puts) {
ops.add(parsePutAnnotation(ae, cachingConfig, put));
}
}

Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(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;
}

这个 lazyInit 就是数组的懒初始化,不解释了:

1
2
3
private <T extends Annotation> Collection<CacheOperation> lazyInit(Collection<CacheOperation> ops) {
return (ops != null ? ops : new ArrayList<CacheOperation>(1));
}

对 Cacheable、CacheEvict、CachePut 进行注解解析

这三者的注解解析大同小异,parseXXXAnnotation(XXX = Cacheable/Evict/Put)的方法实现基本一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CacheXXXOperation parseXXXAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheXXX cacheXXX) {
CacheXXXOperation.Builder builder = new CacheXXXOperation.Builder();

builder.setName(ae.toString());
builder.setCacheNames(cacheXXX.cacheNames());
builder.setCondition(cacheXXX.condition());
builder.setKey(cacheXXX.key());
builder.setKeyGenerator(cacheXXX.keyGenerator());
builder.setCacheManager(cacheXXX.cacheManager());
builder.setCacheResolver(cacheXXX.cacheResolver());
// 此处略有不同根据三种 CacheOperation 参数情况略有不同,下文详说

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

return op;
}

除了上文的 8 个字段外,Cacheable 独有 unless 和 sync 字段,Evict 独有 cacheWide(取自注解的 allEntries 字段) 和 beforeInvocation 字段,Put 独有 unless 字段。

对 Caching 进行注解解析

因为 Caching 本身是一个聚合注解,其定义如下:

1
2
3
4
5
6
7
8
9
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}

所以其需要执行类似 parseCacheAnnotations 的操作,将 Cacheable、CachePut、CacheEvict 三种注解分别转换再聚合起来:

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

SpringCacheAnnotationParser 小结

SpringCacheAnnotationParser 本质上就是将 Cacheable、CacheEvict、CachePut 和 Caching 的注解参数转换成了 CacheOperation 集合的结构。