相关阅读
- Spring Boot源码简析 @EnableAspectJAutoProxy
- Spring Boot源码简析 @EnableAsync
- Spring Boot源码简析 @EnableTransactionManagement
- Spring Cache基础组件 CacheAutoConfiguration
- Spring Cache基础组件 CacheManager
简介
表示启用Spring的注解驱动的缓存管理能力,和@Configuration
配合使用;
必须创建CacheManager
Bean,Spring框架不会提供默认值;@EnableCaching
会根据类型搜索CacheManager
Bean,因此CacheManager
Bean的命名并不重要;
可以实现CachingConfigurer
接口的cacheManager()
方法来创建@EnableCaching
指定的CacheManager
Bean,这种情况下需要明确提供KeyGenerator
(@EnableCaching
会默认提供SimpleKeyGenerator
);如果不需要自定义,可以考虑从CachingConfigurerSupport
扩展,它为所有方法提供了默认实现;
源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
// 代理模式:CGLIB or JDK Interface
// 默认为false,表示基于JDK Interface
// 设置为true,会影响所有需要代理的Spring管理的Bean
boolean proxyTargetClass() default false;
// 缓存应用模式:Proxy or AspectJ
// Proxy模式只允许通过代理拦截调用,不会拦截同一类中的本地调用
// AspectJ模式下,proxyTargetClass()无效,会拦截同一类中的本地调用
AdviceMode mode() default AdviceMode.PROXY;
// 特定连接点应用多个建议时,缓存操作的执行顺序
int order() default Ordered.LOWEST_PRECEDENCE;
}
配置
配合@Configuration
使用,表示开启注解驱动的异步处理;
@Configuration
@EnableCaching
public class AppConfig {
}
简析
通过Import机制 ,@EnableCaching
引入CachingConfigurationSelector.class
,代码如下:
@Import(CachingConfigurationSelector.class)
CachingConfigurationSelector
继承自AdviceModeImportSelector
,AdviceModeImportSelector
实现支持AdviceMode
算法模板,且支持通用的@EnableXxx
注解模式,代码如下:
/**
* 根据AdviceMode选择缓存配置
*/
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);
result.add(AutoProxyRegistrar.class.getName());
result.add(ProxyCachingConfiguration.class.getName());
if (jsr107Present && jcacheImplPresent) {
result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
}
return StringUtils.toStringArray(result);
}
private String[] getAspectJImports() {
List<String> result = new ArrayList<>(2);
result.add(CACHE_ASPECT_CONFIGURATION_CLASS_NAME);
if (jsr107Present && jcacheImplPresent) {
result.add(JCACHE_ASPECT_CONFIGURATION_CLASS_NAME);
}
return StringUtils.toStringArray(result);
}
CachingConfigurationSelector
会根据EnableCaching.mode
引入AutoProxyRegistrar
和ProxyCachingConfiguration
;
AutoProxyRegistrar
AutoProxyRegistrar
实现了ImportBeanDefinitionRegistrar
,用于向容器里注册自动代理创建器AutoProxyCreator
,代码如下:
/**
* importingClassMetadata : 通过Import引入该类的配置类的元数据信息,本例中配置类为CglibAutoProxyConfiguration
* registry : Bean定义容器
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
// 获取配置类的注解列表
// 本例中注解有:
// @Configuration
// @EnableCaching
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
// 遍历注解列表
for (String annoType : annoTypes) {
// 获取注解的属性数据
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
if (candidate == null) {
continue;
}
// 获取mode属性值
Object mode = candidate.get("mode");
// 获取proxyTargetClass属性值
Object proxyTargetClass = candidate.get("proxyTargetClass");
// 过滤出@EnableCaching注解
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
// 本例中,mode为AdviceMode.PROXY,proxyTargetClass为true
if (mode == AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
// 使用subclass-based proxy
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
if (!candidateFound && logger.isWarnEnabled()) {
// 异常LOG记录
String name = getClass().getSimpleName();
logger.warn(String.format("%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occurred as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether.", name, name, name));
}
}
核心逻辑为AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
和AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
,代码如下:
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAutoProxyCreatorIfNecessary(registry, null);
}
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
@Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 判断当前容器中是否存在自动代理创建器的Bean定义
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 获取已存在的Bean定义
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 判断已存在的Bean定义的className和当前是否一致
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 不一致则使用优先级更高的
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 根据当前class创建Bean定义并放入容器中
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 添加Bean定义的proxyTargetClass属性
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
ProxyCachingConfiguration
ProxyCachingConfiguration
继承自AbstractCachingConfiguration
,AbstractCachingConfiguration
是抽象基础配置类,为启用Spring的注解驱动的缓存管理功能提供通用结构;代码如下:
// 注解信息
@Nullable
protected AnnotationAttributes enableCaching;
// 缓存管理器
@Nullable
protected Supplier<CacheManager> cacheManager;
// 缓存解析器
@Nullable
protected Supplier<CacheResolver> cacheResolver;
// 缓存KEY生成器
@Nullable
protected Supplier<KeyGenerator> keyGenerator;
// 缓存错误处理器
@Nullable
protected Supplier<CacheErrorHandler> errorHandler;
/**
* 设置CachingConfigurer
*/
@Autowired(required = false)
void setConfigurers(Collection<CachingConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
// 校验configurers的数量
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配置
CachingConfigurer configurer = configurers.iterator().next();
useCachingConfigurer(configurer);
}
/**
* 根据CachingConfigurer配置
*/
protected void useCachingConfigurer(CachingConfigurer config) {
this.cacheManager = config::cacheManager;
this.cacheResolver = config::cacheResolver;
this.keyGenerator = config::keyGenerator;
this.errorHandler = config::errorHandler;
}
ProxyCachingConfiguration
作为配置类,创建启用Spring基于代理的注解驱动的缓存管理功能所需的基础Bean,具体如下:
BeanFactoryCacheOperationSourceAdvisor
:定义缓存操作的切面;AnnotationCacheOperationSource
:获取定义在类或者方法上的SpringCache相关的注解并将其转换为对应的CacheOperation
属性;CacheInterceptor
:拦截器,当方法调用碰到BeanFactoryCacheOperationSourceAdvisor
定义的切面,就会执行CacheInterceptor
的业务逻辑,该业务逻辑就是缓存的核心业务逻辑;
代码如下:
/**
* 创建切面BeanFactoryCacheOperationSourceAdvisor Bean
*/
@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {
BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
// 设置切点逻辑
advisor.setCacheOperationSource(cacheOperationSource);
// 设置增强
advisor.setAdvice(cacheInterceptor);
if (this.enableCaching != null) {
advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
}
return advisor;
}
/**
* 创建切点CacheOperationSource Bean
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {
// 默认使用AnnotationCacheOperationSource
return new AnnotationCacheOperationSource();
}
/**
* 创建CacheInterceptor Bean
*/
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
CacheInterceptor interceptor = new CacheInterceptor();
// 配置CacheInterceptor
interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
interceptor.setCacheOperationSource(cacheOperationSource);
return interceptor;
}
BeanFactoryCacheOperationSourceAdvisor
Spring Cache基础组件 BeanFactoryCacheOperationSourceAdvisor
AnnotationCacheOperationSource
Spring Cache基础组件 CacheOperationSource
CacheInterceptor
Spring Cache基础组件 CacheInterceptor
常见问题
@Cacheable注解失效
原因
一般是调用同一个类中的@Cacheable注解标注的方法,此时无法经过AOP处理,导致异步执行失效;
解决方法
在调用方法中,从Bean容器中获取本类的代理实例,使用代理实例调用@Cacheable注解标注的方法;
@Autowired
private ApplicationContext applicationContext;
public void invoke() {
System.out.println("当前线程:" + Thread.currentThread().getName());
Demo demo = applicationContext.getBean(Demo.class);
demo.cacheable();
}
@Cacheable(value = "caches", key = "#id")
public List<String> cacheable(Long id) {
// do something
System.out.println("当前线程:" + Thread.currentThread().getName());
return Collections.singletonList(String.valueOf(id));
}