Spring Cache (3) - Pointcut、Advice 和 Advisor

Spring Cache 配置的 Pointcut、Advice 和 Advisor

上篇讲到,Spring Cache 在 AdviceMode.Proxy 模式会注入 AutoProxyRegistrarProxyCachingConfiguration 这两个类,其中 AutoProxyRegistrar 是一个与 cache 功能无关的 AOP 类,已经在上一篇中介绍过。

这篇将详细深入 ProxyCachingConfiguration 的配置类。

ProxyCachingConfiguration 解析

首先可以看到 ProxyCachingConfiguration 继承自 AbstractCachingConfiguration,所以我们先看 AbstractCachingConfiguration 的源码。

注:这个 AbstractCachingConfiguration 也是其他代理模式下配置的基类

ProxyCachingConfiguration 的基类 —— AbstractCachingConfiguration

AbstractCachingConfiguration 源码

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
@Configuration
public abstract class AbstractCachingConfiguration implements ImportAware {

protected AnnotationAttributes enableCaching;

protected CacheManager cacheManager;

protected CacheResolver cacheResolver;

protected KeyGenerator keyGenerator;

protected CacheErrorHandler errorHandler;


@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableCaching = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableCaching.class.getName(), false));
if (this.enableCaching == null) {
throw new IllegalArgumentException(
"@EnableCaching is not present on importing class " + importMetadata.getClassName());
}
}

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

protected void useCachingConfigurer(CachingConfigurer config) {
this.cacheManager = config.cacheManager();
this.cacheResolver = config.cacheResolver();
this.keyGenerator = config.keyGenerator();
this.errorHandler = config.errorHandler();
}

}

这里涉及到一个 CachingConfigurer 的接口,容器内应当注册了 0 个或 1 个 CachingConfigurer,当有 CachingConfigurer 的时候,会为 Cache 功能提供 CacheManagerCacheResolverKeyGeneratorCacheErrorHandler 这四项配置。顺便从容器取到 @EnableCaching 注解的参数保存在 enableCaching 中。

AbstractCachingConfiguration 总结

如果有配置 CachingConfigurer,则将其中的 CacheManagerCacheResolverKeyGeneratorCacheErrorHandler 保存在类中。同时取到 @EnableCaching 注解的参数保存在类中。

ProxyCachingConfiguration 定义

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
36
37
38
39
40
41
@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());
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;
}

}

注:所有的 Configuration 和 Bean 都加上了 @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 以便于被 AutoProxyRegistrar 过滤注册。具体逻辑见上一篇。

配置类一共注入了三个类:CacheOperationSourceCacheInterceptorBeanFactoryCacheOperationSourceAdvisor。额外做的操作就是将基类拿到的 CacheManagerCacheResolverKeyGeneratorCacheErrorHandler 尽可能注入 CacheInterceptor 中,将 EnableCaching 注解中的 order 注入到 BeanFactoryCacheOperationSourceAdvisor 中,并将这三者接线。

我们就按 CacheOperationSourceCacheInterceptorBeanFactoryCacheOperationSourceAdvisor 的顺序看这三个类。

ProxyCachingConfiguration 注入的类

从 CacheOperationSource 到 AnnotationCacheOperationSource

CacheOperationSource 只是一个接口,实际注册的是 AnnotationCacheOperationSource

CacheOperationSource 接口只包含一个 Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) 方法,targetClass 和 method 分别是调用者的类和调用的方法。接口方法应当将 method 上的 Cacheable 之类的注解,将其解析为 CacheOperation 集合并返回。关于 CacheOperation 的细节见下文,点击跳转

AbstractFallbackCacheOperationSource 实现了 CacheOperationSource,并做了一级 CacheOperations 的缓存:

1
2
private final Map<Object, Collection<CacheOperation>> attributeCache =
new ConcurrentHashMap<Object, Collection<CacheOperation>>(1024);

getCacheOperations 方法优先从 attributeCache 缓存拿 CacheOperation 集合,否则使用 computeCacheOperations 方法计算 CacheOperation 集合并放进缓存。

computeCacheOperations 源码如下,过程加了注解:

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
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
// 如果需要的话,不处理非 public 的方法
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}

// 方法可能是在接口上,但是我们需要从目标类获取属性
// 如果目标类为空,则方法不会改变
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// 如果我们处理的是带泛型参数的方法,则需要找到原始方法(桥接方法)
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

// 首先尝试从目标类中的方法上取 CacheOperation
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
if (opDef != null) {
return opDef;
}

// 其次尝试从目标类上取 CacheOperation
opDef = findCacheOperations(specificMethod.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}

if (specificMethod != method) {
// 降级则从原始方法上取 CacheOperation
opDef = findCacheOperations(method);
if (opDef != null) {
return opDef;
}
// 再次降级则从原始方法的类上取 CacheOperation
opDef = findCacheOperations(method.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
}

return null;
}

至于 findCacheOperations(Class<?> clazz) 和 findCacheOperations(Method method) 是由子类实现的抽象方法。

AnnotationCacheOperationSource 继承自 AbstractFallbackCacheOperationSource,根据不同的构造方式可以指定 publicMethodsOnly 和 annotationParsers,这里以默认构造函数为例。

默认构造函数只指定了 publicMethodsOnly 为 true,annotationParsers 只包含一个 SpringCacheAnnotationParser。

下一章我们再来详细看一看 SpringCacheAnnotationParser 这个类,现在我们先暂时知道 SpringCacheAnnotationParser 是一个取到 method 或 clazz 上注解并解析整理成 CacheOperation 集合的解析器类就可以了。

AnnotationCacheOperationSource 关键的功能代码如下:

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
@Override
protected Collection<CacheOperation> findCacheOperations(final Class<?> clazz) {
return determineCacheOperations(new CacheOperationProvider() {
@Override
public Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser) {
return parser.parseCacheAnnotations(clazz);
}
});

}

@Override
protected Collection<CacheOperation> findCacheOperations(final Method method) {
return determineCacheOperations(new CacheOperationProvider() {
@Override
public Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser) {
return parser.parseCacheAnnotations(method);
}
});
}

protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
Collection<CacheOperation> ops = null;
for (CacheAnnotationParser annotationParser : this.annotationParsers) {
Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
if (annOps != null) {
if (ops == null) {
ops = new ArrayList<CacheOperation>();
}
ops.addAll(annOps);
}
}
return ops;
}

因为 annotationParsers 只有一个 SpringCacheAnnotationParser,所以本质上就是执行了一行 return springCacheAnnotationParser.parseCacheAnnotations(method);return springCacheAnnotationParser.parseCacheAnnotations(clazz);

AnnotationCacheOperationSource 小结

我们可以看出,AnnotationCacheOperationSource 最终实现的就是取到方法或目标类上注解并将其解析为 CacheOperation 集合。

CompositeCacheOperationSource

CacheOperationSource 还有一个实现 —— CompositeCacheOperationSource 表示 CacheOperationSource 的聚合,有一个私有的 CacheOperationSource 数组,其 getCacheOperations 方法会将这些 CacheOperationSource 的 getCacheOperations 的结果聚合到一个集合中返回。

CacheOperation

CacheOperation 有三个实现类:CacheEvictOperationCachePutOperationCacheableOperation,对应三种注解的操作。其本身具有以下的字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
private final String name;

private final Set<String> cacheNames;

private final String key;

private final String keyGenerator;

private final String cacheManager;

private final String cacheResolver;

private final String condition;

CacheInterceptor

CacheInterceptor 继承自 CacheAspectSupport,实现了 MethodInterceptor。

MethodInterceptor 的本质是一个增强 Advice。实现 MethodInterceptor 的目的是为了可以被 Advisor 调用。

关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();

CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
@Override
public Object invoke() {
try {
return invocation.proceed();
}
catch (Throwable ex) {
throw new ThrowableWrapper(ex);
}
}
};

try {
return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
}
catch (CacheOperationInvoker.ThrowableWrapper th) {
throw th.getOriginal();
}
}

可以看出,其实就是调用了基类 CacheAspectSupportexecute 方法并拆包了一下基类抛出的异常。

因为此处过于复杂,我们放在下下一章详细讲 CacheAspectSupport 这个类。现在我们只需要知道,CacheInterceptor 提供了一个 Advice,在方法执行前后使用 cacheOperationSource 取到 CacheOperation 集合并执行对应的缓存操作。

BeanFactoryCacheOperationSourceAdvisor

BeanFactoryCacheOperationSourceAdvisor 继承自 AbstractBeanFactoryPointcutAdvisor,其 pointcut 使用的是固定的 CacheOperationSourcePointcut。

CacheOperationSourcePointcut

CacheOperationSourcePointcut 继承自 StaticMethodMatcherPointcut,被 set 了上文的 CacheOperationSource,其 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)));
}

也就是说,能取出 CacheOperation 的方法都会作为切入点。

BeanFactoryCacheOperationSourceAdvisor 小结

也就是说,BeanFactoryCacheOperationSourceAdvisor 会对所有能取出 CacheOperation 的方法执行 CacheInterceptor 这个 Advice。