Spring Boot源码简析 @EnableCaching

相关阅读

简介

表示启用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继承自AdviceModeImportSelectorAdviceModeImportSelector实现支持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引入AutoProxyRegistrarProxyCachingConfiguration

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,具体如下:

  1. BeanFactoryCacheOperationSourceAdvisor:定义缓存操作的切面;
  2. AnnotationCacheOperationSource:获取定义在类或者方法上的SpringCache相关的注解并将其转换为对应的CacheOperation属性;
  3. 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));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值