【Spring Cache】五 从 @EnableCaching 了解 Spring Cache 实现流程
- 前言
- @EnableCaching
- CachingConfigurationSelector
- AutoProxyRegistrar
- ProxyCachingConfiguration
- BeanFactoryCacheOperationSourceAdvisor
- 总结
前言
之前的章节了解了如下内容:
CacheCacheManager相关抽象- 缓存操作相关注解及属性
- 缓存操作对应的
CacheOperation和负责解析对应CacheOperations 的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 AsyncSpring 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对诸如cacheManagercacheResolver等属性进行配置- 这种管理配置类的模式在
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 相关










