默认simplekeygenerator会导致redis key冲突,因其对单参数方法直接使用参数tostring(),如getuserbyid(123)和getorderbyid(123)均生成key“123”,造成缓存互相覆盖。

为什么默认的 SimpleKeyGenerator 会导致 Redis Key 冲突
Spring Boot 的 @Cacheable 默认用 SimpleKeyGenerator,它对无参方法返回 SimpleKey.EMPTY(即一个空对象),对单参数方法直接用参数值的 toString()。问题来了:当多个不同方法都只传一个 Long id,比如 getUserById(123) 和 getOrderById(123),生成的缓存 Key 都是 "123" —— 这会互相覆盖,查用户返回了订单数据。
实现 KeyGenerator 接口时必须重写 generate() 方法
自定义 Key 生成器的核心就是让每个缓存入口有唯一、可读、可追溯的 Key。你需要继承或直接实现 KeyGenerator,并重写 generate(Object target, Method method, Object... params):
-
target是当前被代理的目标对象(如UserServiceImpl实例) -
method是被注解的方法(可用method.getDeclaringClass().getSimpleName() + "#" + method.getName()构建前缀) -
params是实际入参,别直接拼toString(),要处理 null、数组、集合等边界情况
示例片段:
public class CustomKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
String className = target.getClass().getSimpleName();
String methodName = method.getName();
String paramStr = Arrays.stream(params)
.map(p -> p == null ? "null" : p.toString())
.collect(Collectors.joining("_"));
return String.format("%s:%s:%s", className, methodName, paramStr);
}
}
注册自定义 KeyGenerator 后仍不生效?检查这三点
常见失效原因不是代码写错,而是没正确“挂载”到 Spring 上下文:
- 必须声明为
@Bean,且 Bean 名为cacheKeyGenerator(Spring Boot 2.4+ 要求)或通过@Primary显式标记,否则 Spring 仍用默认实例 - 如果项目用了
@EnableCaching,确保它所在配置类能被 ComponentScan 扫描到;若在启动类上,需确认该类包路径覆盖了 KeyGenerator 定义位置 - Redis 缓存管理器(
RedisCacheManager)未显式指定 KeyGenerator 时,才走全局默认;但如果你手动构建了RedisCacheConfiguration,记得调用.keyPrefix(...)或.computePrefixWith(...)—— 此时KeyGenerator不再参与 Key 生成
KeyGenerator 与 RedisCacheConfiguration.keyPrefix 的关系
两者不是互斥,而是协作层级不同:KeyGenerator 负责生成“业务逻辑层 Key”,比如 "UserService:getUserById:123";而 keyPrefix 是 Redis 层的统一前缀,比如 "prod:cache:",最终存入 Redis 的 Key 是两者拼接结果(默认用冒号连接)。所以更安全的做法是:KeyGenerator 专注区分方法和参数,keyPrefix 专注环境/模块隔离。
容易忽略的是:如果你在 RedisCacheConfiguration 中设置了 computePrefixWith(cacheName -> "v2:" + cacheName),那 KeyGenerator 产出的字符串会被整体当作 cacheName 处理——此时它生成的内容就变成了二级前缀,而不是最终 Key 主体。











