默认使用jdkserializationredisserializer会导致redis cli显示乱码(如\xac\xed\x00\x05)且跨语言不可读,反序列化返回linkedhashmap而非原类型;必须改用jackson2jsonredisserializer并显式指定泛型类型(如get("key", user.class)),同时配置objectmapper禁用fail_on_empty_beans、注册javatimemodule、设置setusedefaultmapper(false)。

默认用 JdkSerializationRedisSerializer 存对象,Redis CLI 里全是 \xac\xed\x00\x05,Java 外其他语言根本读不了;更糟的是,反序列化回来是 LinkedHashMap,不是你的 User 类——这不是配置漏了,是默认压根没打算让你“看”或“跨语言互通”。
为什么 RedisTemplate 默认序列化会导致乱码和类型丢失
JdkSerializationRedisSerializer 把对象转成 Java 原生二进制流,结果:
- Redis CLI、RedisInsight 等工具显示为空或乱码(
\xac\xed\x00\x05...) - 其他语言(如 Node.js、Python)无法解析,因为不兼容 JDK 序列化协议
- 如果类没实现
Serializable,运行时报NotSerializableException - 调用
redisTemplate.opsForValue().get("key")返回Object,实际是LinkedHashMap,强转User.class直接ClassCastException
必须用 Jackson2JsonRedisSerializer 并显式传类型参数
换 JSON 序列化只是第一步,关键在反序列化时必须告诉 Jackson “你要还原成什么类”,否则它只能猜成 LinkedHashMap。
- 用
Jackson2JsonRedisSerializer<object></object>初始化 value 序列化器,但不能只传Object.class - 存的时候没问题:
redisTemplate.opsForValue().set("user:1", user) - 取的时候必须指定类型:
redisTemplate.opsForValue().get("user:1", User.class),不能只写get("user:1") - 否则返回的
Object是LinkedHashMap,不是User
自定义 ObjectMapper 时三个关键设置不能少
光换序列化器还不够,Jackson 默认行为会卡住你:
- 必须调
setUseDefaultMapper(false),否则你配的ObjectMapper被忽略 - 必须禁用
SerializationFeature.FAIL_ON_EMPTY_BEANS,否则 Lombok 生成的无参类(比如只有@Data)反序列化失败 - 时间类型要统一处理:加
JavaTimeModule,否则LocalDateTime存进去变字符串、取出来是Map或报错
示例片段:
@Bean
public RedisTemplate<string object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<string object> template = new RedisTemplate();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer<object> serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
template.setUseDefaultMapper(false); // 这行不能漏
template.afterPropertiesSet();
return template;
}</object></string></string>
别用 StringRedisTemplate 曲线救国
有人图省事改用 StringRedisTemplate,手动 json.writeValueAsString() 再存,看似能读——但代价是:
- 丢了类型安全:取出来永远是
String,得自己readValue(..., User.class) - 没法直接操作泛型集合(比如
SetOperations<string user></string>) - 缓存穿透/空值场景下,JSON 字符串和
null边界容易混淆 - 和
@Cacheable集成时,类型擦除问题更难调试
真正麻烦的不是“怎么配”,而是反序列化那一句 get("key", User.class) 容易被忽略——它不像编译期报错,而是在运行时某次取值突然崩掉,而且崩得毫无征兆。











