SpringBoot缓存原理

  • A+

Spring框架提供了便捷的缓存操作。不会对业务调用造成任何干扰,类似于@Transactional支持。不需要手动存取,删除缓存,只需要在类或者方法上进行少量的注解就可以自动完成这些操作。

一.核心概念

1.引入缓存

  • spring-boot中引入缓存
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
    </dependencies>
    

2.基本概念

  • Spring缓存框架中最关键的2个顶级类为org.springframework.cache.CacheManagerorg.springframework.cache.Cache;前者为缓存管理器,管理所有缓存实现,后者定义了缓存的存储规则
  • 我们查看一下实现类结构,可见默认实现了一系列的缓存
    SpringBoot缓存原理
    SpringBoot缓存原理
  • SpringCache支持如下类型缓存管理,当引入各自的spring-starter包就可以使用,SpringCache会依次扫描如下管理器,来加载可用缓存:GenericJCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)EhCache 2.xHazelcastInfinispanCouchbaseRedisCaffeineSimple

二.默认实现

1.SpringBoot缺省状态下的默认实现

  1. SpringCache默认使用内存实现缓存,我们需要关注的是缓存中key的实现规则,和数据存储在哪里。
  2. 在程序启动时可以看到Spring加载关于cache的类,CacheAutoConfiguration,SimpleCacheConfiguration他们定义了spring加载cache的所有流程。
  3. 其中最关键的三个类是SimpleKeyGenerator(缓存存储时key的生成规则),ConcurrentMapCacheManager(缓存管理器),ConcurrentMapCache(缓存的存储库)
  4. SimpleKeyGenerator类
    • 根据CacheAutoConfiguration类引入的CacheAspectSupport类中可以找到springCache加载key的规则
      private SingletonSupplier<KeyGenerator> keyGenerator = SingletonSupplier.of(SimpleKeyGenerator::new);
      
    • SimpleKeyGenerator类中关于key的实现
      public static Object generateKey(Object... params) {
              if (params.length == 0) {
                  return SimpleKey.EMPTY;
              } else {
                  if (params.length == 1) {
                      Object param = params[0];
                      if (param != null && !param.getClass().isArray()) {
                          return param;
                      }
                  }
      
                  return new SimpleKey(params);
              }
          }
      
  5. ConcurrentMapCacheManager类
    • SimpleCacheConfiguration类中定义了CacheManager的具体实现
       ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
      
    • ConcurrentMapCacheManager类中使用ConcurrentMap管理多个cache,并实现了getCache,getCacheNames等管理缓存的方法
      private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap(16);
      
  6. ConcurrentMapCache类
    • ConcurrentMapCache中定义了缓存具体的存取情况,使用ConcurrentMap做存储库,并定义了get,put,evict,clear等处理缓存的方法
      private final ConcurrentMap<Object, Object> store;
      

2. SimpleKeyGenerator中key生成规则

  • Spring Cache默认情况下使用内存作为缓存,其中比较重要的是缓存key的生成,其 SimpleKeyGenerator的实现如下:
    1. 若无参数返回SimpleKey.EMPTY,打印后SimpleKey [],是一个固定值;
    2. 若只有一个参数时,使用此参数进行存储
    3. 若只有多个参数时,使用SimpleKey类其中包含所有参数进行存储,若为具体对象实体,则是实体加包含的所有参数进行存储

三.验证默认实现

1. 获取所有内存中的缓存

  • 获取所有缓存,我们从ApplicationContext,获取ConcurrentMapCacheManager和ConcurrentMapCache实例,将其中的所有缓存数据封装到对象中返回
    @Component
    public class CacheInfo {
    
        //将所有缓存封装到RModel对象中
        @Autowired
        ApplicationContext applicationContext;
        public RModel cache(){
            Map<String,Map<String,String>>   rCache= new HashMap<>();
            ConcurrentMapCacheManager bean = applicationContext.getBean(ConcurrentMapCacheManager.class);//获取内存管理器(cacheManager)
            Collection<String> cacheNames = bean.getCacheNames();//获取指定key内存
            cacheNames.stream().forEach(x->{
                rCache.put(x,new HashMap<>());
                ConcurrentMapCache cache    = (ConcurrentMapCache)bean.getCache(x);//获取指定cache
                ConcurrentMap<Object, Object> nativeCache = cache.getNativeCache();//获取cache存储map
                nativeCache.forEach((y,z)-> rCache.get(x).put(y.toString(),z.toString()));//获取map中的key和value
            });
            return new RModel(null,rCache);
        }
    
        @Data
        @AllArgsConstructor
        class RModel{
            private Object rDate;
            private Map<String,Map<String,String>> cacheDate;
        }
    }
    

2. 验证无参数缓存

  • 验证内存中无参数缓存,结论:①无参数使用SimpleKey []做缓存的key值 ②在同一缓存中存在多个无参数的构造方法,多次请求都会使用SimpleKey []作为key,就会出现脏数据
    1. 我们在service中定义2个没有形参的方法,如下
      @Cacheable(value = "CacheNoParam" )
      public CacheableModel testNoParamCache() {
          return new CacheableModel("Joey",22);
      }
      
      @Cacheable(value = "CacheNoParam" )
      public CacheableModel testNoParamCache2() {
          return new CacheableModel("Chandler",25);
      }
      
    2. 定义Controller方法,继承CacheInfo,打印缓存中的所有数据
       @GetMapping("testNoParamCache")
          public  RModel testNoParamCacheable(){
              CacheableService.CacheableModel cacheableModel = cacheableService.testNoParamCache();
              RModel cache = super.cache();
              cache.setRDate(cacheableModel);
              return cache;
          }
      
          @GetMapping("testNoParamCache2")
          public  RModel testNoParamCacheable2(){
              CacheableService.CacheableModel cacheableModel = cacheableService.testNoParamCache2();
              RModel cache = super.cache();
              cache.setRDate(cacheableModel);
              return cache;
          }
      
    3. 测试,对于testNoParamCache和testNoParamCache2,本应一个返回joey,另一个返回Chandler。但是由于都是无参数方法,使用同一key,所以2者返回了相同数据,如下可见在CacheNoParam的缓存只有一份:
      {
          "cacheDate": {
              "CacheNoParam": {
                  "SimpleKey []": "CacheableService.CacheableModel(name=Chandler, age=25)"
              }
          },
          "rdate": {
              "name": "Chandler",
              "age": 25
          }
      }
      

3. 验证单参数缓存

  • 验证内存中无参数缓存,结论:①使用唯一参数作为key ②若唯一参数为null则缓存为SimpleKey [null]
    1. service中
      @Cacheable(value = "testOneParamCache" )
      public CacheableModel testOneParamCacheMethod(String name) {
          return new CacheableModel(name,44);
      }
      
    2. Controller
      @GetMapping("testOneParamCache")
      public  RModel testOneParamCache(String name){
          CacheableService.CacheableModel cacheableModel = cacheableService.testOneParamCacheMethod(name);
          RModel cache = super.cache();
          cache.setRDate(cacheableModel);
          return cache;
      }
      
    3. 测试,多次请求返回如下缓存结果:
      {
          "cacheDate": {
              "testOneParamCache": {
                  "": "CacheableService.CacheableModel(name=, age=44)",
                  "SimpleKey [null]": "CacheableService.CacheableModel(name=null, age=44)",
                  "Phoebe": "CacheableService.CacheableModel(name=Phoebe, age=44)"
              }
          },
          "rdate": {
              "name": "Phoebe",
              "age": 44
          }
      }
      

4. 验证多参数缓存

  • 验证内存中多参数缓存,结论:①若为基本类型参数,使用SimpleKey对象包含参数作为key,例如SimpleKey [Phoebe,26] ②若为对象,则对象包含参数作为key
    1. service中
      @Cacheable(value = "testMultipleParamCache" )
      public CacheableModel testMultipleParamCacheMethod(String name, Integer age) {
          return new CacheableModel(name,age);
      }
      
    2. Controller
      @GetMapping("testMultipleParamCache")
      public  RModel testMultipleParamCache(String name,Integer age){
          CacheableService.CacheableModel cacheableModel = cacheableService.testMultipleParamCacheMethod(name,age);
          RModel cache = super.cache();
          cache.setRDate(cacheableModel);
          return cache;
      }
      
    3. 测试,多次请求返回如下缓存结果:
      {
          "cacheDate": {
              "testMultipleParamCache": {
                  "SimpleKey [,null]": "CacheableService.CacheableModel(name=, age=null)",
                  "SimpleKey [null,26]": "CacheableService.CacheableModel(name=null, age=26)",
                  "SimpleKey [Phoebe,null]": "CacheableService.CacheableModel(name=Phoebe, age=null)",
                  "SimpleKey [null,null]": "CacheableService.CacheableModel(name=null, age=null)",
                  "SimpleKey [Phoebe,26]": "CacheableService.CacheableModel(name=Phoebe, age=26)"
              }
          },
          "rdate": {
              "name": "",
              "age": null
          }
      }
      

五.总结

zhangfeng

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: