0
点赞
收藏
分享

微信扫一扫

【Spring Cache】五 从 @EnableCaching 了解 Spring Cache 实现流程

覃榜言 2022-02-03 阅读 114

【Spring Cache】五 从 @EnableCaching 了解 Spring Cache 实现流程

前言

之前的章节了解了如下内容:

  • Cache CacheManager 相关抽象
  • 缓存操作相关注解及属性
  • 缓存操作对应的 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 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 两个核心配置类 AutoProxyRegistrarProxyCachingConfiguration

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
InfrastructureAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator
的实现类,它会收集容器中所有 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 对应的 PointcutAdvice
  • 注册了一个 CacheOperationSource 即基于注解解析对应 CacheOperationAnnotationCacheOperationSource,这在之前的章节已经了解
  • 基于对应的属性(这些属性在父类中支持自定义配置)注册了一个 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;
		}
	};

	// ...

}
  • 它是一个 AbstractBeanFactoryPointcutAdvisorAbstractBeanFactoryPointcutAdvisor 在未指定 Advice 的情况下可以基于指定的 beanName 从容器中获取对应的 Advice
  • 同时它支持 Pointcut 的指定
  • 已经了解到它指定了 AdviceCacheInterceptor
  • 此处指定了 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,进而引入了配置类 AutoProxyRegistrarProxyCachingConfiguration
  • AutoProxyRegistrar 注册了一个 InfrastructureAdvisorAutoProxyCreator,支持在 bean 生命周期对应阶段收集容器中指定的 Advisor 进行代理
  • ProxyCachingConfiguration 则提供对上述 Advisor 组件的注册
  • 针对 Spring Cache 注册的 AdvisorBeanFactoryCacheOperationSourceAdvisor,它提供的 Pointcut 是基于 CacheOperationSource 创建的 CacheOperationSourcePointcut
  • CacheOperationSourcePointcut 对类和方法的过滤自然都是基于 CacheOperationSource,此处注册的实例为 AnnotationCacheOperationSource,之前已经了解过
  • BeanFactoryCacheOperationSourceAdvisor 指定了 AdviceCacheInterceptor,它实现了代理行为(即 Cache)的逻辑,内容颇多,会在下个章节单独了解

上一篇:【Spring Cache】四 CacheOperation CacheOperationSource CacheAnnotationParser

下一篇:【Spring Cache】六 CacheInterceptor 相关

举报

相关推荐

0 条评论