likes
comments
collection
share

Spring @Cacheable扩展支持自定义过期时间

作者站长头像
站长
· 阅读数 17

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

Spring @Cacheable扩展支持自定义过期时间

前言

Spring 通过@EnableCaching的方式开启 Cache 的支持, 通过@Cacheable@CacheEvict等一系列注解来实现无侵入式的缓存实现方式, 但是@Cacheable不支持自定义过期时间, 在企业应用中, 绝大部分缓存都是要设置过期时间的, 并且不同的缓存的过期时间也是不同的, 我们想要使用Spring的Cache, 又不能抛弃设置过期时间的功能

Spring @Cacheable扩展支持自定义过期时间

通过查看Spring Cache部分源码, 我们发现Spring提供了 Spring's central cache manager SPI. => CacheManager接口 查看CacheManager接口的所有实现类, 找到了RedisCacheManager, 这不正是我们想要的么

Spring @Cacheable扩展支持自定义过期时间

扩展RedisCacheManager

下面我们就通过扩展RedisCacheManager来实现自定义过期时间支持 我们需要提前制定好使用规则, 如这里我们的规则如下:

  • @Cacheable key 通过 # 进行分割, 取第二个为过期时间, 支持秒级别单位设置

RedisCacheManager

 /**
  * 自定义cacheManager
  */
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    return new RedisCacheManager(
            RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
            // 默认策略,未配置的 key 会使用这个
            this.getRedisCacheConfigurationWithTtl(60),
            // 指定 key 策略
            this.getRedisCacheConfigurationMap()
    );
}

 /**
  * 设置序列化方式
  */
private Jackson2JsonRedisSerializer serializer() {
    // 序列化略......
    return serializer;
}

private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
    Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
    // 通过反射工具获取到需要扫描的包下的所有的class
    validAnnotation(ClassUtil.getClasses(packageName), redisCacheConfigurationMap);
    return redisCacheConfigurationMap;
}

private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
    return RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(
            RedisSerializationContext.SerializationPair.fromSerializer(serializer())).entryTtl(Duration.ofSeconds(seconds));
}

private void validAnnotation(List<Class<?>> clsList, Map<String, RedisCacheConfiguration> redisCacheConfigurationMap) {
    if (CollUtil.isEmpty(clsList)) {
        return;
    }
    clsList.forEach(cls -> {
        Method[] methods = cls.getDeclaredMethods();
        if (Objects.isNull(methods)) {
            return;
        }
        for (Method method : methods) {
            // 方法上有@Cacheable
            Cacheable cacheable = method.getAnnotation(Cacheable.class);
            if (Objects.isNull(cacheable)) {
                continue;
            }
            // 按照定义好的规则截取出过期时间, 并进行设置
            String[] split = cacheable.key().replace("'", "").split("#");
            if (split.length >= 2) {
                Arrays.stream(cacheable.value()).forEach(v -> redisCacheConfigurationMap.put(v, this.getRedisCacheConfigurationWithTtl(Integer.valueOf(split[1]))));
            }
        }
    });
}

代码中的使用

查询数据缓存, 新增/更新/删除直接清除@Cacheable的value分组下所有数据

  • example: 添加缓存 @Cacheable(value = "userSelectByPageCache", key = "'userSelectByPageCache#600#' + #userRequest.pageNum + '_'+ #userRequest.pageSize + '_' +#userRequest.nickname")
    • value: 分组
    • key: redis键, 支持表达式拼接请求参数, 这里截取key中按定义规则的过期时间(秒级)
    • return的结果为redis缓存的值

Spring @Cacheable扩展支持自定义过期时间

  • example: 新增/更新/删除 @CacheEvict(value = "userSelectByPageCache", allEntries = true)
    • value: 等价于@Cacheable的value分组
    • allEntries: true 全部节点

Spring @Cacheable扩展支持自定义过期时间

总结

以上我们通过扩展cacheManager, 按设定好的固定规则设置@Cacheable, 实现了自定义过期时间功能