【Spring Cache】五 从 @EnableCaching 了解 Spring Cache 实现流程
- 前言
- @EnableCaching
- CachingConfigurationSelector
- AutoProxyRegistrar
- ProxyCachingConfiguration
- BeanFactoryCacheOperationSourceAdvisor
- 总结
前言
之前的章节了解了如下内容:
Cache
CacheManager
相关抽象- 缓存操作相关注解及属性
- 缓存操作对应的
CacheOperation
和负责解析对应CacheOperation
s 的CacheOperationSource
相关
以上内容是深入了解 Spring Cache
的铺垫
通常,基于 @EnableCaching
来开启 Spring Cache
能力,本章节从 @EnableCaching
入手,了解下 Spring Cache
的整体实现流程,应该可以或多或少的想到是基于 Spring AOP
实现的
@EnableCaching
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
// 决定是否基于 CGLIB 代理,默认 false
boolean proxyTargetClass() default false;
// 通知的方式:PROXY or ASPECTJ,默认 PROXY 即基于 代理 实现
AdviceMode mode() default AdviceMode.PROXY;
// 可以基于注解排序
int order() default Ordered.LOWEST_PRECEDENCE;
}
proxyTargetClass
属性决定是否基于CGLIB
代理(mode == PROXY
时)mode
属性即通知方式,默认就是基于代理
order
属性说明支持基于注解排序- 关键是
@Import(CachingConfigurationSelector.class)
引入了配置类CachingConfigurationSelector
CachingConfigurationSelector
AdviceModeImportSelector
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
// ...
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 获取泛型注解类
Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
// 获取该注解的属性
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
// 获取 mode 属性
AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
// 子类基于 mode 引入对于的配置类
String[] imports = selectImports(adviceMode);
return imports;
}
/**
* 有子类实现基于 mode 属性引入对应 配置类
*/
@Nullable
protected abstract String[] selectImports(AdviceMode adviceMode);
}
AdviceModeImportSelector
,基于注解 AdviceMode mode
属性引入对应配置类的基类:
- 它是一个
ImportSelector
,因此支持配置类的引入,此处会基于注解的属性解析AdviceMode mode
属性 selectImports
方法交给子类,基于mode
属性引入对应的配置类,Spring Async
Spring Cache
等都有对应的实现- 比如上文
@EnableCaching
引入的CachingConfigurationSelector
就是它对Spring Cache
配置类的引入
CachingConfigurationSelector
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);
// 引入 AutoProxyRegistrar 和 ProxyCachingConfiguration
result.add(AutoProxyRegistrar.class.getName());
result.add(ProxyCachingConfiguration.class.getName());
return StringUtils.toStringArray(result);
}
// ...
}
- 通常情况下
mode
的值就是PROXY
,即基于代理而不是AspectJ
实现 getProxyImports
引入Spring Cache
两个核心配置类AutoProxyRegistrar
和ProxyCachingConfiguration
AutoProxyRegistrar
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
// ...
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
// import 源的所有注解
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
for (String annType : annTypes) {
// 获取目标注解的属性
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null) {
continue;
}
// 获取注解的 mode 和 proxyTargetClass 属性
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
// 注册一个 InfrastructureAdvisorAutoProxyCreator BeanDefinition
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
// 如果 proxyTargetClass == true 则指定 CGLIB 代理
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
}
}
- 它是一个
ImportBeanDefinitionRegistrar
,因此可以持有源配置类的元数据信息,基于此注册对应的BeanDefinition
- 忽略细节,此处最终会基于
AopConfigUtils#registerAutoProxyCreatorIfNecessary
方法注册一个InfrastructureAdvisorAutoProxyCreator
InfrastructureAdvisorAutoProxyCreator:AbstractAdvisorAutoProxyCreator
的实现类,它会收集容器中所有 ROLE == ROLE_INFRASTRUCTURE
的 Advisor,以此进行代理
AopConfigUtils:这个类的作用就是注册对应的 AbstractAdvisorAutoProxyCreator,
它支持优先级的覆盖,优先级依此为:
InfrastructureAdvisorAutoProxyCreator
AspectJAwareAdvisorAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator
举个例子:如果容器中已经注册了
AspectJAwareAdvisorAutoProxyCreator,则再注册
InfrastructureAdvisorAutoProxyCreator 不会降级,而注册
AnnotationAwareAspectJAutoProxyCreator 会升级
关于 InfrastructureAdvisorAutoProxyCreator 的更多细节可
以查看链接:
【源码】Spring AOP 13 原理解读二
关于 AopConfigUtils 的更多细节可以从下文了解到
【源码】Spring AOP 14 原理解读三
ProxyCachingConfiguration
AbstractCachingConfiguration
@Configuration(proxyBeanMethods = false)
public abstract class AbstractCachingConfiguration implements ImportAware {
@Nullable
protected AnnotationAttributes enableCaching;
@Nullable
protected Supplier<CacheManager> cacheManager;
@Nullable
protected Supplier<CacheResolver> cacheResolver;
@Nullable
protected Supplier<KeyGenerator> keyGenerator;
@Nullable
protected Supplier<CacheErrorHandler> errorHandler;
@Autowired
void setConfigurers(ObjectProvider<CachingConfigurer> configurers) {
// 收集容器中用户定义的所有 CachingConfigurer
Supplier<CachingConfigurer> configurer = () -> {
List<CachingConfigurer> candidates = configurers.stream().collect(Collectors.toList());
// 没有就算了
if (CollectionUtils.isEmpty(candidates)) {
return null;
}
// 只允许有一个
if (candidates.size() > 1) {
throw new IllegalStateException("...");
}
return candidates.get(0);
};
// 基于提供的 CachingConfigurer 对属性进行配置
useCachingConfigurer(new CachingConfigurerSupplier(configurer));
}
// ...
}
Spring Cache
的抽象配置基类,它主要是收集容器中唯一的CachingConfigurer
对诸如cacheManager
cacheResolver
等属性进行配置- 这种管理配置类的模式在
Spring
中很常见,比如Spring Async
也是由配置基类AbstractAsyncConfiguration
收集容器中唯一的AsyncConfigurer
配置对应属性 - 因此,我们自定义提供的配置类可以直接实现
CachingConfigurer
或继承CachingConfigurerSupport
类提供对属性的配置
ProxyCachingConfiguration
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {
BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
// 会基于 cacheOperationSource 创建对应的 Pointcut
advisor.setCacheOperationSource(cacheOperationSource);
// 指定了具体的 Advice
advisor.setAdvice(cacheInterceptor);
if (this.enableCaching != null) {
advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {
// 方法不 public 也行
return new AnnotationCacheOperationSource(false);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
CacheInterceptor interceptor = new CacheInterceptor();
/**
* 这些属性在父类中可以基于自定义的 CachingConfigurer 进行配置
*/
interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
interceptor.setCacheOperationSource(cacheOperationSource);
return interceptor;
}
}
这就是最终配置类了,它为容器提供了以下组件:
BeanFactoryCacheOperationSourceAdvisor
,缓存行为代理的Advisor
,基于Spring AOP
专题的了解:Advisor = Pointcut + Advice
,随后会深入了解BeanFactoryCacheOperationSourceAdvisor
对应的Pointcut
和Advice
- 注册了一个
CacheOperationSource
即基于注解解析对应CacheOperation
的AnnotationCacheOperationSource
,这在之前的章节已经了解 - 基于对应的属性(这些属性在父类中支持自定义配置)注册了一个
CacheInterceptor
,它扮演了Advice
的角色,即缓存行为的实现,这会在下个章节单独展开了解
BeanFactoryCacheOperationSourceAdvisor
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Nullable
private CacheOperationSource cacheOperationSource;
// 指定具体 CacheOperationSource 的 CacheOperationSourcePointcut
private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
@Override
@Nullable
protected CacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
};
// ...
}
- 它是一个
AbstractBeanFactoryPointcutAdvisor
,AbstractBeanFactoryPointcutAdvisor
在未指定Advice
的情况下可以基于指定的beanName
从容器中获取对应的Advice
- 同时它支持
Pointcut
的指定 - 已经了解到它指定了
Advice
为CacheInterceptor
- 此处指定了
Pointcut
是基于CacheOperationSource
创建的CacheOperationSourcePointcut
- 关于
Advisor
的更多细节,可以参考下文:
【源码】Spring AOP 5 Advisor
CacheOperationSourcePointcut
/**
* StaticMethodMatcherPointcut 是一个 MethodMatcher 又是一个 Pointcut
* 因此可以基于 Pointcut 拓展 ClassFilter 的能力
*/
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
// 指定 ClassFilter
protected CacheOperationSourcePointcut() {
setClassFilter(new CacheOperationSourceClassFilter());
}
// MethodMatcher#match 委托 CacheOperationSource 实现
@Override
public boolean matches(Method method, Class<?> targetClass) {
CacheOperationSource cas = getCacheOperationSource();
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}
//...
// 实现类指定具体的 CacheOperationSource
@Nullable
protected abstract CacheOperationSource getCacheOperationSource();
// 内部类维护对应的 ClassFilter
private class CacheOperationSourceClassFilter implements ClassFilter {
// ClassFilter#match 委托 CacheOperationSource 实现
@Override
public boolean matches(Class<?> clazz) {
// 不代理 CacheManager
if (CacheManager.class.isAssignableFrom(clazz)) {
return false;
}
// 基于 CacheOperationSource#isCandidateClass 过滤
CacheOperationSource cas = getCacheOperationSource();
return (cas == null || cas.isCandidateClass(clazz));
}
}
}
- 它是一个
StaticMethodMatcherPointcut
,因此它额外拓展了ClassFilter
的能力,允许指定一个ClassFilter
- 作为
StaticMethodMatcher
,它对方法的匹配基于CacheOperationSource
方法完成:即CacheOperationSource
能在目标方法上解析到CacheOperation
才匹配 - 指定的
ClassFilter
为内部类CacheOperationSourceClassFilter
,它的匹配委托CacheOperationSource#isCandidateClass
完成 - 关于
Pointcut
的更多细节,可以参考下文
【源码】Spring AOP 4 Pointcut
demo
public class BeanFactoryCacheOperationSourceAdvisorDemo {
public static class CacheTarget {
@Cacheable
public String a(String a) {
return "a";
}
@CachePut
public String b(String b) {
return "b";
}
}
@Test
public void test() throws NoSuchMethodException {
BeanFactoryCacheOperationSourceAdvisor advisor
= new BeanFactoryCacheOperationSourceAdvisor();
advisor.setCacheOperationSource(new AnnotationCacheOperationSource());
// ClassFilter#matches 是委托 AnnotationCacheOperationSource 实现的
System.out.println(advisor.getPointcut()
.getClassFilter()
.matches(CacheTarget.class));
// MethodMatcher#matches 是委托 AnnotationCacheOperationSource 实现的
System.out.println(advisor.getPointcut()
.getMethodMatcher()
.matches(
CacheTarget.class.getMethod("a", String.class)
, CacheTarget.class
));
}
}
结合示例体会 BeanFactoryCacheOperationSourceAdvisor
Pointcut
部分的能力
总结
本章节,从 @EnableCaching
入手,大致的总结如下:
- 它引入了配置类
CachingConfigurationSelector
,进而引入了配置类AutoProxyRegistrar
和ProxyCachingConfiguration
AutoProxyRegistrar
注册了一个InfrastructureAdvisorAutoProxyCreator
,支持在bean
生命周期对应阶段收集容器中指定的Advisor
进行代理ProxyCachingConfiguration
则提供对上述Advisor
组件的注册- 针对
Spring Cache
注册的Advisor
是BeanFactoryCacheOperationSourceAdvisor
,它提供的Pointcut
是基于CacheOperationSource
创建的CacheOperationSourcePointcut
CacheOperationSourcePointcut
对类和方法的过滤自然都是基于CacheOperationSource
,此处注册的实例为AnnotationCacheOperationSource
,之前已经了解过BeanFactoryCacheOperationSourceAdvisor
指定了Advice
是CacheInterceptor
,它实现了代理行为(即Cache
)的逻辑,内容颇多,会在下个章节单独了解
上一篇:【Spring Cache】四 CacheOperation CacheOperationSource CacheAnnotationParser
下一篇:【Spring Cache】六 CacheInterceptor 相关