@CacheEvict不生效最常见原因是key不一致:@Cacheable默认生成SimpleKey(如SimpleKey[1]),而@CacheEvict未配key时直接用参数值(如"1"),导致Redis中无法匹配删除。

@CacheEvict不生效,最常见原因是key不一致
缓存写入和清除用的Redis key对不上,是90%以上@CacheEvict失效的根本原因。比如@Cacheable没配key,默认生成SimpleKey(如SimpleKey [1]),而@CacheEvict没配key时却把参数原样当key(如"1"),两者在Redis里根本不是同一个key。
验证方法:用redis-cli连上Redis,执行KEYS *users*(或SCAN 0 MATCH *users* COUNT 100)查看实际存储的key格式;再对比@CacheEvict生成的key是否匹配。
- 写入时key为
SimpleKey [id, name]→ 清除时必须显式写key = "T(org.springframework.cache.interceptor.SimpleKey).new(#id, #name)" - 想统一用固定key → 两边都配
key = "'userConfig'" - 用SpEL时注意类型:传
Long但表达式写#id.toString()可能多出引号,导致key变成"\"123\""
allEntries = true在Redis里清不掉数据?检查cacheName前缀和序列化
@CacheEvict(value = "users", allEntries = true)调用的是RedisCache.clear(),底层走DefaultRedisCacheWriter.clean(),它会拼一个pattern去SCAN匹配key。如果项目给所有Redis key加了业务前缀(比如myapp:users::1),但clean()方法没把前缀加进去,就会SCAN不到任何key,看似“清空失败”。
典型现象:DEBUG进DefaultRedisCacheWriter.clean(),看到pattern是"users:*",但实际Redis里key是"myapp:users::1" —— 完全不匹配。
- 不能直接继承
DefaultRedisCacheWriter(它是final类) - 得自己写个代理类,重写
clean(),把前缀注入到pattern里 - 或者改用
redisTemplate手动删:redisTemplate.delete(redisTemplate.keys("myapp:users::*"))(注意生产禁用KEYS,改用SCAN分批)
beforeInvocation = true时缓存仍残留?看方法是否真的执行了
beforeInvocation = true表示清除动作在方法体执行前触发。但如果方法抛了异常、被AOP拦截、或根本没走到该方法(比如被@ConditionalOnProperty跳过),@CacheEvict压根不会运行。
更隐蔽的情况:方法是private或final,Spring AOP无法代理,注解直接被忽略 —— 此时连日志都不会打,就像没写一样。
- 确保方法是public,且所在类由Spring容器管理(非
new XXX()手动创建) - 检查调用链是否跨了代理边界:比如Service A调Service B的
@CacheEvict方法,但B是this.xxx()自调用,AOP失效 - 加
@CacheEvict(..., condition = "true")排除SpEL解析失败的干扰
缓存没过期,但@CacheEvict也不起作用?确认RedisCacheManager配置
Spring Boot的RedisCacheManager如果配置了setUsePrefix(true)(默认true),它会在所有key前加cacheName前缀,比如"users" → "users::1"。但如果你手动用redisTemplate.opsForValue().set("1", ...)写入,就没这个前缀,@CacheEvict自然找不到。
另外,spring.cache.redis.time-to-live只影响@Cacheable首次写入的TTL,不影响@CacheEvict行为 —— 但它会影响你误以为“缓存还在是因为没过期”,其实只是没删干净。
- 统一用Spring Cache操作,别混用
redisTemplate直写 - 检查
RedisCacheConfiguration是否启用了key prefix:cacheConfig.usePrefix() - 如果用了自定义
RedisCacheWriter,确认它和RedisCacheManager用的是同一套序列化器
真实环境里最容易被忽略的,是key生成逻辑和cache writer底层pattern构造这两层脱节 —— 看似配置一样,实则Redis里存的和删的压根不在一个命名空间。


















