根本原因是jackson反序列化时缺乏类型信息,只能退化为linkedhashmap;必须通过genericjackson2jsonredisserializer配合启用defaulttyping的objectmapper,或使用jackson2jsonredisserializer指定具体类型,才能正确还原实体类。

为什么取出来总是 LinkedHashMap 而不是你的实体类
这不是 Redis 搞错了,是 Jackson 反序列化时根本不知道该还原成哪个类。比如你用 Jackson2JsonRedisSerializer(Object.class) 初始化 value 序列化器,Jackson 就只能按 JSON 结构映射成 LinkedHashMap——它不猜、也不推断,只做最保守的通用映射。
常见触发场景:
- 存的是
User,但序列化器声明为Jackson2JsonRedisSerializer(Object.class) - 用了
GenericJackson2JsonRedisSerializer,但没配好ObjectMapper的类型白名单,导致反序列化失败后退化为LinkedHashMap - Spring Boot 2.6+ 升级后,默认禁用了
DefaultTyping,原来能跑的GenericJackson2JsonRedisSerializer突然开始返回LinkedHashMap
Jackson2JsonRedisSerializer 和 GenericJackson2JsonRedisSerializer 怎么选
名字有误导性:GenericJackson2JsonRedisSerializer 才是真正支持多态、带类型提示的那个;Jackson2JsonRedisSerializer 是纯 JSON 模式,不写也不读 @class 字段。
选型建议:
- 业务只存一种固定类型(如全是
User),用new Jackson2JsonRedisSerializer(User.class),简单安全 - 需要混存多种类型(
User、Order、Config),必须用GenericJackson2JsonRedisSerializer,并配套注册具体类型 - 别用
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)—— 这等于开ALLOW_ALL,有反序列化 RCE 风险 - 推荐注册整个包:
mapper.registerSubtypes(PackageVersion.create("com.example.dto", null)),再调用activateDefaultTyping(..., JsonTypeInfo.As.PROPERTY, JsonTypeInfo.Id.CLASS)
泛型声明 RedisTemplate<string user></string> 为什么不管用
RedisTemplate<k v></k> 的泛型只在编译期起作用,运行时决定反序列化结果的,是 valueSerializer 配置,不是泛型本身。
典型错误写法:
redisTemplate.opsForValue().set("user:1001", user);
User user = (User) redisTemplate.opsForValue().get("user:1001"); // 危险!强制转型
正确做法:
- 确保
valueSerializer明确指定类型,例如new Jackson2JsonRedisSerializer(User.class) - 避免直接强转
(User) get(...),改用类型安全的ValueOperations<string user></string>实例 - 如果必须用
Object泛型,那就老老实实检查返回值是不是instanceof LinkedHashMap,再手动转换
热部署(devtools / RestartClassLoader)引发的 ClassCastException
报错形如:com.example.User cannot be cast to com.example.User,连类名都一样,却报类型转换失败——这是典型的类加载器分裂问题。
原因很直接:Spring Boot DevTools 启动时会用 RestartClassLoader 加载业务类,而 Redis 反序列化出来的对象由 AppClassLoader(或另一个 RestartClassLoader 实例)加载,JVM 认为它们是两个不同的类。
临时绕过方式:
- 禁用热部署:
spring.devtools.restart.enabled=false - 加
serialVersionUID字段(治标不治本,仅缓解部分场景) - 更彻底的解法:停掉 devtools,改用远程调试或 IDE 的 hotswap 支持(如 JRebel)
这个坑往往被忽略,因为本地开发时一切正常,一上测试环境或 CI 构建后就复现——关键点在于确认类是否真的由同一个 ClassLoader 加载。











