泛型丢失不是 redistemplate 的 bug,而是 jackson 默认反序列化时无法还原泛型类型信息所致,必须显式传入 typereference 或改用 genericjackson2jsonredisserializer。

直接说结论:泛型丢失不是 RedisTemplate 的 bug,而是 Jackson 默认反序列化时无法还原泛型类型信息导致的,必须显式传入 TypeReference 或使用 GenericJackson2JsonRedisSerializer 替代 Jackson2JsonRedisSerializer。
为什么 get() 返回 LinkedHashMap 而不是你的目标类?
这是最典型的表象——比如你存的是 List<user></user>,取出来却变成 LinkedHashMap,强转就报 ClassCastException。根本原因是:Jackson2JsonRedisSerializer 构造时只传了 Object.class(或某个具体类),它反序列化 JSON 时不带泛型上下文,JSON 本身又不携带类型元数据,Jackson 只能退化为 Map。
- 存的时候用
redisTemplate.opsForValue().set("users", userList),其中userList是List<user></user> - 取的时候用
redisTemplate.opsForValue().get("users"),返回值静态类型是Object,运行时其实是LinkedHashMap实例 - 即使你在代码里写
(List<user>) value</user>,JVM 也拦不住——泛型在编译后已擦除,类型转换纯靠 runtime 检查
两种可靠解法:选对序列化器 or 手动指定类型
不要试图靠“加个 @JsonTypeInfo”或者改 ObjectMapper 配置来一劳永逸解决泛型丢失——那治标不治本,且容易引发跨服务兼容问题。
- 方案一:换用
GenericJackson2JsonRedisSerializer(推荐用于通用 value 场景)
它内部会把类型信息编码进 JSON 字符串(如{"@class":"java.util.ArrayList","@items":[{"@class":"com.example.User",...}]}),反序列化时自动还原。但注意:它要求所有被序列化的类必须有无参构造器,且开启enableDefaultTyping的代价是 JSON 体积略大、可读性下降 - 方案二:保持用
Jackson2JsonRedisSerializer,但每次get()时手动传TypeReference
例如:redisTemplate.opsForValue().get("users", new TypeReference<list>>() {})</list>。这个方法不侵入序列化器配置,适合类型明确、调用点少的场景
key 和 hash key 必须用 StringRedisSerializer
很多人只改 value 序列化器,却忘了 key。如果 key 也用 Jackson2JsonRedisSerializer,会导致 redis-cli 里看到 key 是乱码(实际是 JSON 字节数组),KEYS * 查不到,DEL 也删不掉——因为 key 的字节序列和你预期的字符串不匹配。
- 务必设置:
template.setKeySerializer(new StringRedisSerializer()) - hash 结构的 field 同理:
template.setHashKeySerializer(new StringRedisSerializer()) - value 和 hash value 才是你要控制泛型还原的地方,用 Jackson 相关序列化器
最容易被忽略的坑:ObjectMapper 共享与线程安全
如果你在配置类里 new 出一个 ObjectMapper,然后反复 set 给多个 Jackson2JsonRedisSerializer 实例,要注意:ObjectMapper 是线程安全的,但它的配置(比如 setVisibility、activateDefaultTyping)一旦设了就全局生效。多个序列化器共用同一个 ObjectMapper,可能互相干扰。
更稳妥的做法是每个 Jackson2JsonRedisSerializer 持有独立的 ObjectMapper 实例,或者至少确保它们的配置逻辑互不冲突。特别是 activateDefaultTyping 这种开关,开或关必须全项目统一,否则同一份 JSON 在不同地方反序列化结果可能不一致。











