潘志宝
2024-11-21 d338b50afd6504a9676f0a26b3ecbcc844483e7c
提交 | 用户 | 时间
e7c126 1 package com.iailab.framework.redis.core;
H 2
3 import cn.hutool.core.util.NumberUtil;
4 import cn.hutool.core.util.StrUtil;
5 import org.springframework.cache.annotation.Cacheable;
6 import org.springframework.data.redis.cache.RedisCache;
7 import org.springframework.data.redis.cache.RedisCacheConfiguration;
8 import org.springframework.data.redis.cache.RedisCacheManager;
9 import org.springframework.data.redis.cache.RedisCacheWriter;
10
11 import java.time.Duration;
12
13 /**
14  * 支持自定义过期时间的 {@link RedisCacheManager} 实现类
15  *
16  * 在 {@link Cacheable#cacheNames()} 格式为 "key#ttl" 时,# 后面的 ttl 为过期时间。
17  * 单位为最后一个字母(支持的单位有:d 天,h 小时,m 分钟,s 秒),默认单位为 s 秒
18  *
19  * @author iailab
20  */
21 public class TimeoutRedisCacheManager extends RedisCacheManager {
22
23     private static final String SPLIT = "#";
24
25     public TimeoutRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
26         super(cacheWriter, defaultCacheConfiguration);
27     }
28
29     @Override
30     protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
31         if (StrUtil.isEmpty(name)) {
32             return super.createRedisCache(name, cacheConfig);
33         }
34         // 如果使用 # 分隔,大小不为 2,则说明不使用自定义过期时间
35         String[] names = StrUtil.splitToArray(name, SPLIT);
36         if (names.length != 2) {
37             return super.createRedisCache(name, cacheConfig);
38         }
39
40         // 核心:通过修改 cacheConfig 的过期时间,实现自定义过期时间
41         if (cacheConfig != null) {
42             // 移除 # 后面的 : 以及后面的内容,避免影响解析
43             String ttlStr = StrUtil.subBefore(names[1], StrUtil.COLON, false); // 获得 ttlStr 时间部分
44             names[1] = StrUtil.subAfter(names[1], ttlStr, false); // 移除掉 ttlStr 时间部分
45             // 解析时间
46             Duration duration = parseDuration(ttlStr);
47             cacheConfig = cacheConfig.entryTtl(duration);
48         }
49
50         // 创建 RedisCache 对象,需要忽略掉 ttlStr
51         return super.createRedisCache(names[0] + names[1], cacheConfig);
52     }
53
54     /**
55      * 解析过期时间 Duration
56      *
57      * @param ttlStr 过期时间字符串
58      * @return 过期时间 Duration
59      */
60     private Duration parseDuration(String ttlStr) {
61         String timeUnit = StrUtil.subSuf(ttlStr, -1);
62         switch (timeUnit) {
63             case "d":
64                 return Duration.ofDays(removeDurationSuffix(ttlStr));
65             case "h":
66                 return Duration.ofHours(removeDurationSuffix(ttlStr));
67             case "m":
68                 return Duration.ofMinutes(removeDurationSuffix(ttlStr));
69             case "s":
70                 return Duration.ofSeconds(removeDurationSuffix(ttlStr));
71             default:
72                 return Duration.ofSeconds(Long.parseLong(ttlStr));
73         }
74     }
75
76     /**
77      * 移除多余的后缀,返回具体的时间
78      *
79      * @param ttlStr 过期时间字符串
80      * @return 时间
81      */
82     private Long removeDurationSuffix(String ttlStr) {
83         return NumberUtil.parseLong(StrUtil.sub(ttlStr, 0, ttlStr.length() - 1));
84     }
85
86 }