Spring Cache 缓存注解这样用,实在是太香了!

2023年 11月 9日 153.3k 0

作者最近在开发公司项目时使用到 Redis 缓存,并在翻看前人代码时,看到了一种关于 @Cacheable 注解的自定义缓存有效期的解决方案,感觉比较实用,因此作者自己拓展完善了一番后分享给各位。

Spring 缓存常规配置

Spring Cache 框架给我们提供了 @Cacheable 注解用于缓存方法返回内容。但是 @Cacheable 注解不能定义缓存有效期。这样的话在一些需要自定义缓存有效期的场景就不太实用。

按照 Spring Cache 框架给我们提供的 RedisCacheManager 实现,只能在全局设置缓存有效期。这里给大家看一个常规的 CacheConfig 缓存配置类,代码如下,

@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
    ...

    private RedisSerializer keySerializer() {
        return new StringRedisSerializer();
    }

    private RedisSerializer valueSerializer() {
        return new GenericFastJsonRedisSerializer();
    }

    public static final String CACHE_PREFIX = "crowd:";

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 配置序列化(解决乱码的问题)
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //设置key为String
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                //设置value为自动转Json的Object
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
                .computePrefixWith(name -> CACHE_PREFIX + name  + ":")
                .entryTtl(Duration.ofSeconds(600));
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(Objects.requireNonNull(redisConnectionFactory));
        return new RedisCacheManager(redisCacheWriter, config);
    }
}

这里面简单对 RedisCacheConfiguration 缓存配置做一下说明:

  • serializeKeysWith():设置 Redis 的 key 的序列化规则。
  • erializeValuesWith():设置 Redis 的 value 的序列化规则。
  • computePrefixWith():计算 Redis 的 key 前缀。
  • entryTtl():全局设置 @Cacheable 注解缓存的有效期。
  • 那么使用如上配置生成的 Redis 缓存 key 名称是什么样得嘞?这里用开源项目 crowd-admin 的 ConfigServiceImpl 类下 getValueByKey(String key) 方法举例,

    @Cacheable(value = "configCache", key = "#root.methodName + '_' + #root.args[0]")
    @Override
    public String getValueByKey(String key) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("configKey", key);
        Config config = getOne(wrapper);
        if (config == null) {
            return null;
        }
        return config.getConfigValue();
    }

    执行此方法后,Redis 中缓存 key 名称如下,

    crowd:configCache:getValueByKey_sys.name

    图片图片

    TTL 过期时间是 287,跟我们全局设置的 300 秒基本是一致的。此时假如我们想把 getValueByKey 方法的缓存有效期单独设置为 600 秒,那我们该如何操作嘞?

    @Cacheable 注解默认是没有提供有关缓存有效期设置的。想要单独修改 getValueByKey 方法的缓存有效期只能修改全局的缓存有效期。那么有没有别的方法能够为 getValueByKey 方法单独设置缓存有效期嘞?当然是有的,大家请往下看。

    自定义 MyRedisCacheManager 缓存

    其实我们可以通过自定义 MyRedisCacheManager 类继承 Spring Cache 提供的 RedisCacheManager 类后,重写 createRedisCache(String name, RedisCacheConfiguration cacheConfig) 方法,代码如下,

    public class MyRedisCacheManager extends RedisCacheManager {
        public MyRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
            super(cacheWriter, defaultCacheConfiguration);
        }
    
        @Override
        protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
            String[] array = StringUtils.split(name, "#");
            name = array[0];
             // 解析 @Cacheable 注解的 value 属性用以单独设置有效期
            if (array.length > 1) {
                long ttl = Long.parseLong(array[1]);
                cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
            }
            return super.createRedisCache(name, cacheConfig);
        }
    }

    MyRedisCacheManager 类逻辑如下,

  • 继承 Spring Cache 提供的 RedisCacheManager 类。
  • 重写 createRedisCache(String name, RedisCacheConfiguration cacheConfig) 方法。
  • 解析 name 参数,根据 # 字符串进行分割,获取缓存 key 名称以及缓存有效期。
  • 接着我们修改下 CacheConfig 类的 cacheManager 方法用以使用 MyRedisCacheManager 类。代码如下,

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return new MyRedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), defaultCacheConfig());
    }
    
    private RedisCacheConfiguration defaultCacheConfig() {
        return RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
                .computePrefixWith(name -> CACHE_PREFIX + name  + ":")
                .entryTtl(Duration.ofSeconds(600));
    }

    最后我们修改下 @Cacheable 注解使用方式,在原有 value 属性的 configCache 值后添加 #600,单独标识缓存有效期。代码如下,

    @Cacheable(value = "configCache#600", key = "#root.methodName + '_' + #root.args[0]")
    @Override
    public String getValueByKey(String key) {
       ...
    }

    看下 getValueByKey 方法生成的 Redis 缓存 key 有效期是多久。如下,

    图片图片

    OK,看到是 590 秒有效期后,我们就大功告成了,希望本文能对大家有所帮助😁🎇😎。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论