默认simplekeygenerator导致redis key冲突,因其忽略类名方法名,仅基于参数值生成key:单参调用tostring()、无参返回不可序列化empty、多参拼接难读字符串,造成跨业务缓存覆盖。

为什么默认 SimpleKeyGenerator 会导致 Redis Key 冲突
因为 SimpleKeyGenerator 完全忽略类名和方法名,只看参数值:单参数方法直接调用 toString(),getUserById(123) 和 getOrderById(123) 都生成 "123";无参方法返回不可序列化的 SimpleKey.EMPTY;多参则拼成类似 SimpleKey[1,"abc",true] 这种难读、难调试、无法区分业务上下文的字符串。Redis 里 key 一撞,缓存就互相覆盖——查用户返回订单,不是 bug,是设计如此,但线上不能忍。
实现 KeyGenerator 接口时 generate() 必须处理的边界情况
重写 generate(Object target, Method method, Object params) 不能只拼 params.toString(),否则 null、数组、集合、嵌套对象都会出问题:
-
params是Object[],需遍历处理,不能直接Arrays.toString(params) - null 值必须显式转为
"null",否则toString()报 NPE - 数组或
Collection类型建议用ObjectMapper序列化为 JSON 字符串,避免[1,2]和Arrays.asList(1,2)生成相同 key - 推荐用下划线
_分隔段,不用冒号:或点.,防止和 Redis 命名空间习惯(如user:1001)冲突 - 类名优先用
target.getClass().getSimpleName(),比getName()简洁,避免包名过长污染 key
注册自定义 KeyGenerator 后不生效的三个硬性检查点
代码写对了,缓存还是走默认逻辑?大概率卡在这三处:
- Bean 名必须是
cacheKeyGenerator—— Spring Boot 2.4+ 强制要求,起名myKeyGenerator或wiselyKeyGenerator都无效 - 如果加了
@Primary,必须确保该 Bean 是唯一被标记为 primary 的KeyGenerator,否则 Spring 会因歧义 fallback 到默认 - 如果你手动构建了
RedisCacheConfiguration,比如调用了.keyPrefix("xxx")或.computePrefixWith(...),那KeyGenerator就彻底被绕过了,此时 key 生成完全由 prefix + 参数决定,和你的 generate 方法无关
一个健壮、可直接用的 KeyGenerator 示例
这个版本兼顾可读性、唯一性和 null/集合安全:
public class CustomKeyGenerator implements KeyGenerator {
private final ObjectMapper objectMapper = new ObjectMapper();
@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 -> {
if (p == null) return "null";
if (p instanceof Collection || p.getClass().isArray()) {
try {
return objectMapper.writeValueAsString(p);
} catch (Exception e) {
return p.toString(); // fallback
}
}
return p.toString();
})
.collect(Collectors.joining("_"));
return String.format("%s_%s_%s", className, methodName, paramStr);
}
}
注意:这个类要声明为 @Bean,且名字必须是 cacheKeyGenerator;如果项目已启用 Jackson,无需额外依赖;若没引入,ObjectMapper 可换成 Gson 或简单降级为 p.getClass().getSimpleName() + "@" + p.hashCode()。
最易被忽略的是:一旦你调用了 RedisCacheConfiguration.keyPrefix(),整个 KeyGenerator 就失效了——这点在多级缓存或需要统一前缀的场景里,特别容易踩坑。











