在使用 Spring Boot + Redis + Jackson 的项目中,很多人(尤其是初学者)第一次遇到下面这种报错时,常常一脸懵:
com.fasterxml.jackson.databind.exc.InvalidTypeIdException:
Could not resolve type id 'cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO'
as a subtype of java.lang.Object: no such class found
这其实是一个非常常见的 Redis 缓存反序列化失败 问题。
别慌,我们一起来彻底搞懂!
🧩 一、问题现象
系统启动正常,但一旦访问某些接口、登录后台、或者加载系统配置时,
就会在日志中爆出长长的堆栈错误,核心是这句:
Could not resolve type id 'xxx.YourClassName' as a subtype of `java.lang.Object`: no such class found
通俗地说就是:
反序列化 Redis 缓存时,找不到之前序列化进去的那个类了。
🔍 二、为什么会这样?
Spring Boot 默认使用 Jackson 序列化对象到 Redis。
为了能在反序列化时知道"原始类类型",
Jackson 会在 JSON 中加一个隐藏字段:
json
{
"@class": "cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO",
"clientId": "default",
...
}
当系统再次读取这个缓存时,它会尝试加载这个类。
但是如果出现以下情况之一👇:
情况 | 举例 |
---|---|
✅ 类被重命名或包名修改 | 旧类叫 OAuth2ClientDO ,新版本包改动了 |
✅ 版本升级了 | 不同版本的 Yudao、RuoYi、Spring 模块结构变动 |
✅ 你清除了部分代码但 Redis 还存着旧对象 | 开发环境中残留缓存 |
✅ 多个微服务版本不一致 | 一个老版本写缓存,一个新版本读缓存 |
Jackson 就会发现:
"咦?我找不到这个类呀!"
于是就抛出
InvalidTypeIdException
。
⚠️ 三、错误本质:Redis 里有旧缓存,反序列化失败
这不是你的代码错了。
而是 Redis 存了旧版本类的数据,新版本的程序读不懂。
就好像你用新版 Word 打开一个被损坏的老文件一样。
✅ 四、最简单的解决办法(推荐)
🧹 方案 1:直接清空 Redis 缓存
开发环境或者测试环境下,最推荐的解决方式:
bash
redis-cli
> FLUSHALL
或者只删掉有问题的部分缓存(更安全):
bash
redis-cli keys "*oauth2*" | xargs redis-cli del
重启项目,一切恢复正常。
🧱 五、如果是线上环境,不敢清空怎么办?
没问题,你可以加一个兼容旧类名的配置。
🧩 方案 2:在 Redis 序列化配置里增加类型映射
java
@Bean
public RedisSerializer<Object> redisSerializer() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY
);
// 添加类名映射(旧 -> 新)
SimpleModule module = new SimpleModule();
module.setMixInAnnotation(
com.your.new.package.OAuth2ClientDO.class,
OldOAuth2ClientMixin.class
);
objectMapper.registerModule(module);
return new GenericJackson2JsonRedisSerializer(objectMapper);
}
// 定义一个空的 mixin,目的是告诉 Jackson 旧类别名
@JsonTypeName("cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO")
private static class OldOAuth2ClientMixin {}
这样 Jackson 遇到旧类名时,就能正确反序列化到新类。
🚫 六、从源头解决:禁用 @class 信息(更安全的序列化)
如果你不想再被这个问题坑,可以换一种 Redis 序列化方式:
java
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// ✅ 使用 JSON 序列化,但不包含 @class 信息
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setDefaultSerializer(serializer);
template.afterPropertiesSet();
return template;
}
或者使用更安全的 StringRedisSerializer
/ JSON
自定义序列化,只保存你需要的数据字段。
🧠 七、快速排查技巧
检查点 | 命令 |
---|---|
查看 Redis 里有哪些 key | redis-cli keys "*" |
查看某个 key 内容 | redis-cli get your_key |
删除问题 key | redis-cli del your_key |
📘 八、总结
解决方式 | 适用场景 | 推荐指数 |
---|---|---|
🧹 清空 Redis 缓存 | 开发/测试环境 | ⭐⭐⭐⭐⭐ |
🔧 删除单个 Key | 已知问题缓存 | ⭐⭐⭐⭐ |
🧱 增加类型映射 | 线上系统兼容性 | ⭐⭐⭐ |
🚫 禁用 @class 类型信息 | 长期安全策略 | ⭐⭐⭐⭐ |
💬 九、结语
这个问题其实不是"系统坏了",
只是因为 Redis 缓存的类信息和当前代码不一致。
一句话总结:
类路径变了,但 Redis 还活在过去。
遇到这种错误,不要慌,先清缓存就对了 ✅
------记住这一点,你就比 90% 的人更懂 Spring Boot 与 Redis 的运行机制!