Spring的缓存管理器

       在spring项目中提供了CacheManager接口来定义缓存管理器,这样各个不同的缓存就可以实现它来提供管理器的功能了,在spring-boot-starter-data-redis.jar中自动配置了RedisCacheManager(实现了CacheManager接口),可以通过设置spring.cache.cache-names属性在启动时创建其缓存,并且可以使用spring.cache.redis.*属性配置缓存默认值。例如,以下配置创建的cache1和cache2缓存的生存时间为10分钟:
Java代码 
spring.cache.cache-names=cache1,cache2  
spring.cache.redis.time-to-live=600000  

 也可以自己定义RedisCacheManager来完全控制配置。如果您要定制序列化策略,这可能很有用

@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public CacheManager cacheManager(@Autowired RedisConnectionFactory redisConnectionFactory){
        ObjectMapper om = new ObjectMapper();
        // 设置在反序列化时忽略在JSON字符串中存在,而在Java中不存在的属性
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 解决jackson2无法反序列化LocalDateTime的问题
        om.registerModule(new JavaTimeModule());
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        // 不存在的字段,不被序列化
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        var jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(om,Object.class);

        return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                        .prefixCacheNameWith("canary:")
                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                        .entryTtl(Duration.ofMinutes(5 + randomTTL()))) //给缓存的key添加随机时间
                .build();
    }
    private int randomTTL(){
        return ThreadLocalRandom.current().nextInt(1000 * 60 * 5);
    }
}

@EnableCaching表示spring IoC容器启动了缓存机制。

缓存注解简介

配置了缓存管理器后,Spring就允许用注解的方式使用缓存了。使用的注解有4个

注解   描述
@Cachable 表明在进入方法之前,Spring会先从缓存服务器中查找对应key的缓存值,如果找到缓存值,那么Spring将不再调用方法,而是将缓存值直接返回给调用者;如果没有找到缓存值,那么spring就会执行方法,将最后的结果通过key保存到缓存服务器中。
 @CachePut  Spring会将该方法的返回值缓存到缓存服务器中。spring不会事先去缓存服务器中查找,而是直接执行方法,然后缓存。
 @CacheEvict  移除缓存对应key的值
 @Caching  分组注解,能够同时应用与其它缓存

       上述注解都可以标注在类或者方法上,如果放在类上,表示对所有的方法都有效;如果放在方法上,则只对该方法有效。在大部分情况下或放置在方法上。一般而言,对于查询,我们会考虑使用@Cacheabel,对于插入和修改,考虑使用@CachePut,对于删除操作,我们会考虑使用@CacheEvict。

注解@Cacheable和@CachePut

属性 配置类型 描述
value String[] 缓存的名称,可以引用多个缓存管理器
condition String Spring表达式,true缓存,false不缓存
key String Spring表达式,计算对应缓存的key
unless String Spring表达式,如果值为true,则不缓存结果

       先了解一些Spring表达式和缓存注解之间的约定,通过这些约定去引用方法的参数和返回值的内容,使得其注入key所定义的Spring表达式的结果中。

表达式 描述 备注
#root.args 定义传递给缓存方法的参数 不常用
#root.caches 数组,该方法执行是对应的缓存名称  
#root.target 执行缓存的目标对象  
#root.targetClass 目标对象的类,同#root.target.class  
#root.method 缓存方法  
#root.methodName 缓存方法的名称,同#root.method.name  
#result 方法返回结果值,还可以使用Spring表达式进一步读取其属性 该表达式不能用于注解@Cacheable。因为该注解的方法可能不会被执行,这样返回值就无从谈起了
#Argument 任意方法的参数,可以通过方法本身的名称或者下标去定义 比如get(Long id),想读取id这个参数,可以写为#id,或者#a0、#p0

 案例
Java代码 
@Service  
@Transactional  
public class DepartmentServiceImpl implements DepartmentService {  
    @Autowired private DepartmentRepository repository;  
     /** 
     *  使用@CachePut表示无论如何都会执行方法,将方法返回值再保存到缓存中 
     * 使用在插入数据的地方,表示插入到数据库后会同步插入到Redis缓存中 
     *  通过value引用缓存管理器,通过key定义键 
     * @param department 部门对象 
     * @return 部门,主键回填 
     */  
    @Override @CachePut(value="redisCacheManager",key="'redis_dept_' + #result.id")  
    public Department save(Department department) {  
        repository.save(department);  
    }  
   /** 
    * @CacheEvict删除缓存的key 
    */  
    @Override @CacheEvict(value="redisCacheManager",key="'redis_dept_' + #id")  
    public void remove(Integer id) {  
        //repository.deleteById(id);  
        repository.remove(id);  
    }  
  
    /** 
     *  使用@Cacheable定义缓存策略 
     *  当缓存中有值,则返回缓存数据,否则访问方法得到数据 
     *  通过value引用缓存管理器,通过key定义键 
     * @param id 部门id 
     * @return 部门 
     */  
    @Override   
    @Transactional(propagation = Propagation.NOT_SUPPORTED)  
    @Cacheable(value = "redisCacheManager",key = "'redis_dept_' + #id")  
    public Department find(Integer id) {  
        return repository.findById(id).orElse(null);  
    }  
  
    @Override @CachePut(value="redisCacheManager",key="'redis_dept_' + #department.id")  
    public Department edit(Department department) {  
        repository.saveAndFlush(department);  
    }  
}  
 注解@CacheEvict
      注解@CacheEvict主要是为了移除缓存对应的键值对,主要用于那些删除操作
属性 配置类型 描述
value String[] 缓存的名称,可以引用多个缓存管理器
condition String Spring表达式,true缓存,false不缓存
key String Spring表达式,计算对应缓存的key
allEntries boolean true删除特定缓存的所有键值对,默认为false【慎用】
beforeInvocation boolean 在特定方法后移除缓存,true在方法前移除,false则在方法后移除,默认false
不适用缓存的方法
 
       适用缓存的前提是高命中率,如根据各种条件动态查询的方法返回值会根据查询条件而多样化,导致其不确定性和命中率低下,对于这样的场景,适用缓存并不能有效提高性能,故这样的场景就不再适用缓存了。
 
posted @ 2021-07-07 15:49  Tiger-Adan  阅读(557)  评论(0编辑  收藏  举报