@Cacheable 不支持 timeToLive 属性,控制 TTL 只有三处:全局统一配置(RedisCacheConfiguration.entryTtl)、按 cacheName 配多个 RedisCacheManager、或直接用 RedisTemplate 手动设置。

@Cacheable 根本不支持 timeToLive 属性,写上去直接编译失败。Spring 原生注解里压根没这个字段,所有声称“加个属性就能设 TTL”的博客或 IDE 自动补全建议,都是错的。你真正能控制过期时间的地方,只有三处:全局统一配置、按 cacheName 拆多个 RedisCacheManager、或者绕过 Spring Cache 直接用 RedisTemplate。
全局 TTL 只能靠 RedisCacheConfiguration.entryTtl()
所有走同一个 RedisCacheManager 的缓存项,TTL 必然一致。配置方式是创建 RedisCacheManager Bean 时显式调用 .entryTtl(Duration.ofMinutes(5)):
- 必须配合
cacheDefaults(config)使用,否则entryTtl()不生效,缓存变成永不过期 - 如果用了
RedisCacheManager.builder(redisConnectionFactory())却漏掉cacheDefaults(),你的 TTL 设置就白写了 - 这个配置对所有
@Cacheable(cacheNames = "xxx")都生效,无法区分 key 或参数
按 cacheName 区分 TTL:配多个 RedisCacheManager 实例
想让 "user" 缓存过期 2 小时、"config" 过期 10 分钟?只能靠多个独立的 RedisCacheManager Bean:
- 每个
@Bean返回一个新实例,绑定各自的RedisCacheConfiguration(含不同entryTtl()) -
@Cacheable(cacheManager = "userCacheManager")中的字符串必须和@Bean方法名完全一致(大小写敏感) - 每个
RedisCacheManager默认创建自己的 Lettuce 连接池,高并发下注意max-active配置,避免连接耗尽 - 别指望一个
RedisCacheManager内部“智能识别”不同 cacheName 并自动切 TTL —— 它做不到
强时效场景必须用 RedisTemplate.set() 手动控制
登录 token、验证码、临时配置这类数据,生命周期语义明确,不能依赖抽象层。直接用 RedisTemplate 是唯一可控路径:
- 推荐写法:
redisTemplate.opsForValue().set(key, value, duration, TimeUnit.SECONDS) - 别用
StringRedisTemplate.opsForValue().set(key, value, duration, TimeUnit.SECONDS)后再expire()——SETEX在 key 存在时会覆盖值但不重置 TTL,行为不可控 - 如果
value是对象,确认redisTemplate的valueSerializer是GenericJackson2JsonRedisSerializer或兼容类型,否则读出来是null或乱码 -
expire()返回false?说明 key 根本没写进去,得先检查set()是否成功
真正麻烦的不是怎么配,而是混合策略下的维护成本:多个 RedisCacheManager 要管连接池、序列化、前缀;RedisTemplate 要自己处理空值、穿透防护、反序列化异常。业务越复杂,越容易在某次上线后发现某个 cacheName 突然不刷新了——十有八九是 @Cacheable(cacheManager = "...") 字符串写错了,或者新 RedisCacheManager 漏掉了 cacheDefaults()。











