Redis注解缓存那点事儿,怎么根据动态规则来特别存储和实现原理解析
- 问答
- 2025-12-24 03:48:43
- 2
关于Redis注解缓存那点事儿,特别是怎么根据动态规则来特别存储,以及这背后的原理,咱们就聊得直白一点,这事儿说白了,就是想让我们放在缓存里的数据更“聪明”,不是死板地按照一个固定规则来,而是能根据实际情况,比如不同的用户、不同的参数,来灵活地决定存到哪里、存多久。
第一部分:为啥需要动态规则?—— 死板缓存的麻烦
我们平时最常用的缓存注解,比如Spring里的@Cacheable,通常是这么用的:@Cacheable(value = "users", key = "#userId"),这意思是,把查询结果塞进一个叫users的缓存区域里,并用方法参数userId的值作为键,这招对付简单场景没问题,但现实很复杂。

你的系统既要给普通App用,又要给管理后台用,查询用户信息的方法都是getUserById(Long userId),但你对后台的响应速度要求极高,希望后台查询的数据在Redis里存久一点(比如1小时),而App端的可以存短一点(比如5分钟),如果只用固定的value和key,你没法区分这次调用是来自App还是后台,缓存时间只能定死一个值,这就不能满足不同场景的需求。
再比如,你的缓存可能想根据业务状态动态生成键名,除了用户ID,你还想加上用户的类型或者状态,形成一个更精细的键,像user:VIP:123和user:normal:456,这样在清理某一类用户缓存时会特别方便,这些,都需要动态规则。

第二部分:怎么实现动态存储?—— 玩转Key和TTL
核心思路就是两个:动态键(Key) 和 动态过期时间(TTL),在Spring Cache抽象中,这主要通过SpEL(Spring表达式语言)来实现。

-
动态Key: 就像前面提到的,你可以在
key属性里写SpEL表达式,而不仅仅是简单的参数名。- 简单组合:
key = "'user:' + #user.type + ':' + #user.id",这样就能生成user:VIP:123这样的键,单引号是为了表明user:是一个字符串常量。 - 调用方法:你甚至可以调用你自定义的方法来生成键,比如
key = "T(com.your.util.KeyGenerator).generateUserKey(#userId, #userType)",这样就把生成键的逻辑完全交给了KeyGenerator类的一个静态方法,灵活性极高。
- 简单组合:
-
动态TTL(过期时间): 这是实现“特别存储”更关键的一步,默认的
@Cacheable不支持直接设置TTL,我们需要用到@Cacheable的cacheManager或cacheResolver属性,配合自定义的缓存配置。- 原理是:Spring Cache抽象里有一个
CacheManager来管理各种缓存(比如RedisCache),我们可以自己定义一个RedisCacheManager,让它能根据缓存名称(也就是@Cacheable里的value)来返回不同配置(尤其是不同TTL)的RedisCache实例。 - 具体做法(简化描述):你先定义一个配置类,创建多个
RedisCacheConfiguration对象,每个对象设置不同的过期时间(entryTtl),在你自定义的RedisCacheManager中,当一个缓存名称(比如cache_admin)被请求时,就返回配置了1小时TTL的RedisCache;当另一个缓存名称(比如cache_app)被请求时,就返回配置了5分钟TTL的RedisCache。 - 那么在使用时,你只需要在注解里指定不同的
value即可:后台方法用@Cacheable(value = "cache_admin", key = "#userId"),App方法用@Cacheable(value = "cache_app", key = "#userId"),这样,虽然键都是userId,但它们被存到了逻辑上不同的缓存区域,并拥有了不同的存活时间。
- 原理是:Spring Cache抽象里有一个
第三部分:原理解析——Spring和Redis如何配合干这件事
理解了上面的做法,原理就清晰了,这背后是Spring Cache抽象的一套拦截和代理机制。
- 拦截:当你在一个方法上加了
@Cacheable注解,Spring会为这个类创建一个代理(Proxy),当你调用这个方法时,这个调用会被代理拦截下来。 - 生成Key:代理会解析注解里的SpEL表达式,根据当前的方法参数,计算出生效的缓存键(Key),这就是动态Key的实现时刻。
- 查询缓存:代理拿着这个Key,去问
CacheManager:“给我一个叫cache_admin的缓存实例。”CacheManager根据我们之前的自定义配置,返回一个设置了1小时TTL的RedisCache对象。 - 缓存命中判断:
RedisCache会拿着生成的Key去Redis里查询是否存在,如果存在(缓存命中),直接返回数据,根本不会执行你写的真实方法,如果不存在(缓存未命中),则继续。 - 执行方法并缓存:代理会去执行你写的真实方法,拿到返回结果。
- 存储缓存:代理将这个结果交给
RedisCache,RedisCache再通过RedisTemplate等客户端,将这个Key-Value对写入Redis服务器。最关键的一步在这里:因为我们之前为cache_admin这个缓存区域配置了1小时的TTL,所以RedisCache在执行Redis的SET命令时,会带上过期时间参数(例如SET key value EX 3600),这样,这条数据在Redis里自然就会在1小时后自动过期,动态TTL就是这样生效的。
整个过程就像是Spring定义了一套流程(拦截、生成Key、查缓存、存缓存),而我们把其中“如何生成Key”和“缓存的具体配置(如TTL)”这两个可以插拔的部分,通过SpEL和自定义CacheManager给动态化了,这样一来,缓存就不再是死板的,而是可以根据我们的业务规则灵活变化,实现“特别存储”的目的。
本文由钊智敏于2025-12-24发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/67315.html
