package com.iailab.framework.redis.core; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.redis.cache.RedisCache; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import java.time.Duration; /** * 支持自定义过期时间的 {@link RedisCacheManager} 实现类 * * 在 {@link Cacheable#cacheNames()} 格式为 "key#ttl" 时,# 后面的 ttl 为过期时间。 * 单位为最后一个字母(支持的单位有:d 天,h 小时,m 分钟,s 秒),默认单位为 s 秒 * * @author iailab */ public class TimeoutRedisCacheManager extends RedisCacheManager { private static final String SPLIT = "#"; public TimeoutRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) { super(cacheWriter, defaultCacheConfiguration); } @Override protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { if (StrUtil.isEmpty(name)) { return super.createRedisCache(name, cacheConfig); } // 如果使用 # 分隔,大小不为 2,则说明不使用自定义过期时间 String[] names = StrUtil.splitToArray(name, SPLIT); if (names.length != 2) { return super.createRedisCache(name, cacheConfig); } // 核心:通过修改 cacheConfig 的过期时间,实现自定义过期时间 if (cacheConfig != null) { // 移除 # 后面的 : 以及后面的内容,避免影响解析 String ttlStr = StrUtil.subBefore(names[1], StrUtil.COLON, false); // 获得 ttlStr 时间部分 names[1] = StrUtil.subAfter(names[1], ttlStr, false); // 移除掉 ttlStr 时间部分 // 解析时间 Duration duration = parseDuration(ttlStr); cacheConfig = cacheConfig.entryTtl(duration); } // 创建 RedisCache 对象,需要忽略掉 ttlStr return super.createRedisCache(names[0] + names[1], cacheConfig); } /** * 解析过期时间 Duration * * @param ttlStr 过期时间字符串 * @return 过期时间 Duration */ private Duration parseDuration(String ttlStr) { String timeUnit = StrUtil.subSuf(ttlStr, -1); switch (timeUnit) { case "d": return Duration.ofDays(removeDurationSuffix(ttlStr)); case "h": return Duration.ofHours(removeDurationSuffix(ttlStr)); case "m": return Duration.ofMinutes(removeDurationSuffix(ttlStr)); case "s": return Duration.ofSeconds(removeDurationSuffix(ttlStr)); default: return Duration.ofSeconds(Long.parseLong(ttlStr)); } } /** * 移除多余的后缀,返回具体的时间 * * @param ttlStr 过期时间字符串 * @return 时间 */ private Long removeDurationSuffix(String ttlStr) { return NumberUtil.parseLong(StrUtil.sub(ttlStr, 0, ttlStr.length() - 1)); } }