springboot缓存

Spring缓存

缓存注解和概念

缓存SPEL

使用

在Springboot主配置类上打开注解Cache使用@EnableCaching,然后就可以在方法上使用上图中的各种缓存注解。

  • @Cacheable
    • cacheNames/values:指定缓存的名字,将方法的返回值放在哪个缓存中,可以使用数组
    • key:缓存数据使用的key,可以使用SPEL表达式来,如果不指定,默认使用方法参数的值
    • keyGenerator:key的生成器,指定key生成器的组件id,key/keyGenerator 二选一
    • cacheManager:指定缓存管理器或者使用 cacheResolver:指定获取解析器
    • condition:在指定条件下才缓存
    • unless:当unless表达式为true时不缓存 eg:‘#result==null’
    • sync:是否使用异步
  • @CachePut既调用方法,也将方法结果更新至缓存
    • @CachePut需要指定key与写入缓存时的@Cacheable key要一样(@CachePut可以使用#{result}而@Cacheable不行,因为@CachePut是运行在方法执行后的)
  • @CacheEvict删除缓存数据
    • allEntries 是否删除当前缓存组件下所有缓存 (默认false)
    • beforeInvocation 缓存的删除是否在方法执行之前(默认false在方法之后)
  • @Caching定义复杂的缓存规则
  • @CacheConfig 类缓存配置 eg:配置整个类所有缓存名

原理

  • 自动配置类 CacheAutoConfiguration

  • 缓存的配置类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
    mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
    mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
    mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
    mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
    mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
    mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
    mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
    mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
    mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
    mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
    MAPPINGS = Collections.unmodifiableMap(mappings);

    默认生效SimpleCacheConfiguration给容器中注册了一个ConcurrentMapCacheManager可以创建和查找ConcurrentMapCache类型的缓存。而ConcurrentMapCache就是将数据放在了ConcurrentMap中。

  • 运行流程

    • 方法之前,先去查询cache ,根据cacheNames获取(CacheManager获取响应的缓存,第一次获取缓存会自动创建)

    • 去缓存中查找缓存的内容 调用lookup,使用一个key从ConcurrentMap取值,默认key为方法参数的值。key是按照某种策略生成的,默认使用SimpleKeyGenerator生成key

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public static Object generateKey(Object... params) {
      if (params.length == 0) {
      return SimpleKey.EMPTY;
      }
      if (params.length == 1) {
      Object param = params[0];
      if (param != null && !param.getClass().isArray()) {
      return param;
      }
      }
      return new SimpleKey(params);
      }
      如果没参数就使用SimpleKey.EMPTY,如果有1个就用第一个参数,如果多个就用SimpleKey包装后的对象
  • 没有查到缓存,就调用目标方法,然后将目标方法返回的结果放进缓存中

    总结:@Cacheable标记的方法,在执行之前会去检查缓存中是否有key对应的值,如果没有择运行方法,把返回值放入缓存中。

    首先得到CacheManager得到Cache组件,key是使用keyGenerator生成的,默认使用SimpleKeyGenerator

Redis

Springboot默认使用SimpleCacheConfiguration最终将数据存放在ConcurrentMap

使用

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

RedisAutoConfiguration会自动帮我在IOC注入stringRedisTemplateRedisTemplate

stringRedisTemplate用来处理非KV数据

  • String类型 stringRedisTemplate.opsForValue()
  • list类型 stringRedisTemplate.opsForList()
  • Set类型 stringRedisTemplate.opsForSet()
  • hash散列 stringRedisTemplate.opsForHash()
  • Zset有序集合 stringRedisTemplate.opsForZSet()

RedisTemplate用来处理KV数据

  • 改变序列化的规则,选择用Jackson2JsonRedisSerializer转换为JSON格式,默认为JDK的序列化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Configuration
    public class MyRedisConfig {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
    throws UnknownHostException {
    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory);
    template.setDefaultSerializer(new Jackson2JsonRedisSerializer(Employee.class));
    return template;
    }

    }