spring boot 源码解析34-spring boot集成spring cache(基于EHcache)

前言

本文在上篇文章的基础上做出修改,使其使用Ehcache.

Ehcache集成

  1. 在pom文件中加入如下配置:

    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
    </dependency>
  2. 在src/main/resources 目录下加入ehcache.xml.代码如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
    
    <!-- <diskStore>:当内存缓存中对象数量超过maxElementsInMemory时,将缓存对象写到磁盘缓存中(需对象实现序列化接口)。 -->
    <!-- <diskStore path="">:用来配置磁盘缓存使用的物理路径,Ehcache磁盘缓存使用的文件后缀名是*.data和*.index。 -->
    <diskStore path="java.io.tmpdir/ehcache" />
    
    <!-- 默认缓存 -->
    <!-- maxElementsInMemory: 内存缓存中最多可以存放的元素数量,若放入Cache中的元素超过这个数值,则有以下两种情况。 1)若overflowToDisk=true,则会将Cache中多出的元素放入磁盘文件中。 2)若overflowToDisk=false,则根据memoryStoreEvictionPolicy策略替换Cache中原有的元素。 默认策略是LRU(最近最少使用) -->
    <!-- Eternal:缓存中对象是否永久有效,即是否永驻内存,true时将忽略timeToIdleSeconds和timeToLiveSeconds。 -->
    <!-- timeToIdleSeconds:缓存数据在失效前的允许闲置时间(单位:秒),仅当eternal=false时使用,默认值是0表示可闲置时间无穷大, 此为可选属性即访问这个cache中元素的最大间隔时间,若超过这个时间没有访问此Cache中的某个元素,那么此元素将被从Cache中清除。 -->
    <!-- timeToLiveSeconds:缓存数据在失效前的允许存活时间(单位:秒),仅当eternal=false时使用,默认值是0表示可存活时间无穷大, 即Cache中的某元素从创建到清楚的生存时间,也就是说从创建开始计时,当超过这个时间时,此元素将从Cache中清除。 -->
    <!-- overflowToDisk:内存不足时,是否启用磁盘缓存(即内存中对象数量达到maxElementsInMemory时,Ehcache会将对象写到磁盘中), 会根据标签中path值查找对应的属性值,写入磁盘的文件会放在path文件夹下,文件的名称是cache的名称,后缀名是data。 -->     
    <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" />
    
     <!-- 缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里)。 -->
     <cache name="users" maxEntriesLocalHeap="200" timeToLiveSeconds="600">
     </cache>
    </ehcache>  

    关于Ehcache的配置,可参考如下链接:

    Ehcache整合spring配置

    Ehcache配置详解及CacheManager使用

    此时,我们就修改好了,这也能够体验到spring cache 的威力,不依赖于底层实现.

自动装配

EhCacheCacheManager的自动装配是在EhCacheCacheConfiguration中.

这里先提1个问题,spring boot 中默认支持的是ConcurrentMapCacheManager,现在我们再加入ehcache的依赖,不就有两个了吗? 这样有问题吗?

答案: 不会.原因如下:

  1. chche的自动装配的总入口是在CacheAutoConfiguration中,该类通过@Import(CacheConfigurationImportSelector.class) 导入了CacheConfigurationImportSelector. 由于该类是ImportSelector的类型,因此会执行其selectImports方法,将其返回值接着调用ConfigurationClassParser#processImports处理.其selectImports代码如下:

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                CacheType[] types = CacheType.values();
                String[] imports = new String[types.length];
                for (int i = 0; i < types.length; i++) {
                    imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
                }
                return imports;
    }

    遍历CacheType的枚举集合,依次获得其对应的配置类,加入到imports中 最后的返回值如下:

    1. GenericCacheConfiguration.class
    2. JCacheCacheConfiguration.class
    3. EhCacheCacheConfiguration.class
    4. HazelcastCacheConfiguration.class
    5. InfinispanCacheConfiguration.class
    6. CouchbaseCacheConfiguration
    7. RedisCacheConfiguration
    8. CaffeineCacheConfiguration
    9. GuavaCacheConfiguration
    10. SimpleCacheConfiguration
    11. NoOpCacheConfiguration
  2. 对于 GenericCacheConfiguration来说,其注解如下:

    @Configuration
    @ConditionalOnBean(Cache.class)
    @ConditionalOnMissingBean(CacheManager.class)
    @Conditional(CacheCondition.class)

    该类会生效.

  3. 对于 JCacheCacheConfiguration, HazelcastCacheConfiguration,… GuavaCacheConfiguration来说,都需要其相关的类在类路径下存在. 因此,在默认情况下在PARSE_CONFIGURATION阶段生效的配置有:

    GenericCacheConfiguration, EhCacheCacheConfiguration, SimpleCacheConfiguration,NoOpCacheConfiguration

  4. 最后,执行ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass(ConfigurationClass, TrackedConditionEvaluator)时:

    1. GenericCacheConfiguration: 由于@ConditionalOnBean(Cache.class)是在REGISTER_BEAN阶段时执行并且在beanFactory中不存在Cache类型的bean.因此该类不进行注册.证明:查询/autoconfig,其中有关GenericCacheConfiguration的结果如下:

      GenericCacheConfiguration: {
      notMatched: [
      {
      condition: "OnBeanCondition",
      message: "@ConditionalOnBean (types: org.springframework.cache.Cache; SearchStrategy: all) did not find any beans"
      }
      ],
      matched: [
      {
      condition: "CacheCondition",
      message: "Cache org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration automatic cache type"
      }
      ]
      }
    2. RedisCacheConfiguration:同样,不满足@ConditionalOnBean(RedisTemplate.class)的条件,因此不会执行注册.证明:查询/autoconfig,其中有关GenericCacheConfiguration的结果如下:

      RedisCacheConfiguration: {
      notMatched: [
      {
      condition: "OnBeanCondition",
      message: "@ConditionalOnBean (types: org.springframework.data.redis.core.RedisTemplate; SearchStrategy: all) did not find any beans"
      }
      ],
      matched: [
      {
      condition: "CacheCondition",
      message: "Cache org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration automatic cache type"
      }
      ]
      }
    3. SimpleCacheConfiguration–> 不满足@ConditionalOnMissingBean(CacheManager.class)条件(因为EhCacheCacheConfiguration中配置了),证明:

      {
      notMatched: [
      {
      condition: "OnBeanCondition",
      message: "@ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) found bean 'cacheManager'"
      }
      ],
      matched: [
      {
      condition: "CacheCondition",
      message: "Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type"
      }
      ]
      }
    4. NoOpCacheConfiguration–> 不满足@ConditionalOnMissingBean(CacheManager.class)条件(因为EhCacheCacheConfiguration中配置了),证明:

      NoOpCacheConfiguration: {
      notMatched: [
      {
      condition: "OnBeanCondition",
      message: "@ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) found bean 'cacheManager'"
      }
      ],
      matched: [
      {
      condition: "CacheCondition",
      message: "Cache org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration automatic cache type"
      }
      ]

    因此,最后生效的是EhCacheCacheManager

  1. 回到正题上来,EhCacheCacheConfiguration有如下注解:

    @Configuration
    @ConditionalOnClass({ Cache.class, EhCacheCacheManager.class })
    @ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)
    @Conditional({ CacheCondition.class,
            EhCacheCacheConfiguration.ConfigAvailableCondition.class })
    • @Configuration –> 配置类
    • @ConditionalOnClass({ Cache.class, EhCacheCacheManager.class })–> 在当前类路径下存在 Cache.class, EhCacheCacheManager.class 时生效
    • @ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)–> beanFactory中不存在org.springframework.cache.CacheManager类型的bean时生效
    • @Conditional({ CacheCondition.class,
      EhCacheCacheConfiguration.ConfigAvailableCondition.class })–>

      1. CacheCondition –> 默认返回true,我们在上篇文章中分析过了
      2. EhCacheCacheConfiguration.ConfigAvailableCondition–> 如果配置有spring.cache.ehcache.config 则返回匹配,或者 如果在类路径下存在ehcache.xml则返回匹配.由于我们在类路径下创建了ehcache.xml,因此,返回匹配.
  2. 在该类中声明了2个@Bean方法:

    1. ehCacheCacheManager,代码如下:

      @Bean
      @ConditionalOnMissingBean
      public CacheManager ehCacheCacheManager() {
          Resource location = this.cacheProperties
                  .resolveConfigLocation(this.cacheProperties.getEhcache().getConfig());
          if (location != null) {
              return EhCacheManagerUtils.buildCacheManager(location);
          }
          return EhCacheManagerUtils.buildCacheManager();
      }
      • @Bean –> 注册1个id为ehCacheCacheManager,类型为CacheManager的bean
      • @ConditionalOnMissingBean–> 当BeanFactory中不存在CacheManager类型的bean时生效

      该方法的逻辑如下:

      1. 获得spring.cache.ehcache.config 配置的路径
      2. 如果配置了,则加载指定的路径创建CacheManager,否则,读取默认EhCache的文件路径:/ehcache.xml,/ehcache-failsafe.xml

      由于我们没有配置spring.cache.ehcache.config,因此会读取ehcache.xml

    2. cacheManager,代码如下:

      @Bean
      public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager) {
          return this.customizers.customize(new EhCacheCacheManager(ehCacheCacheManager));
      }
      • @Bean –> 注册1个id为cacheManager,类型为EhCacheCacheManager的bean

      方法的逻辑如下:

      1. 实例化EhCacheCacheManager
      2. 根据CacheManagerCustomizers来做个性化配置.由于spring boot 没有装配CacheManagerCustomizer,因此该方法相当于空操作.
    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_26000415/article/details/79143616
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞