RedisTemplate默认使用JdkSerializationRedisSerializer导致乱码、跨语言不互通及NotSerializableException;必须替换为Jackson2JsonRedisSerializer并显式指定类型反序列化,否则取值为LinkedHashMap。

RedisTemplate默认序列化为什么存进去是乱码或报错
Spring Boot的RedisTemplate默认用JdkSerializationRedisSerializer,它把对象转成Java原生二进制字节流。结果就是:存进去一堆不可读的\xac\xed\x00\x05,其他语言或Redis CLI查不到结构,JSON客户端(比如RedisInsight)直接显示为空或解析失败;更糟的是,如果类没实现Serializable,运行时抛NotSerializableException。
- 根本原因不是“配置错了”,而是默认策略压根没打算让人看、也没打算跨语言互通
- 只要你要存POJO、要和前端/其他服务共享数据、要用Redis命令行调试,就必须换序列化器
- 别碰
StringRedisTemplate去“曲线救国”——它只支持String,对对象只能手动json.writeValueAsString(),丢了类型安全和自动反序列化能力
怎么让RedisTemplate用Jackson2JsonRedisSerializer存对象
核心是替换valueSerializer,同时确保key还是字符串(避免key也变JSON),并且处理好泛型类型擦除导致的反序列化失败问题。
- 必须显式指定
ObjectMapper,并禁用FAIL_ON_EMPTY_BEANS(否则Lombok生成的无参类可能报错) -
setUseDefaultMapper(false)一定要调,否则Jackson会忽略你配的ObjectMapper - 反序列化时得传具体类型,比如
redisTemplate.opsForValue().get("user:1", User.class),不能只写get("user:1")——后者返回Object,实际是LinkedHashMap,不是你的User - 示例配置片段:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
((Jackson2JsonRedisSerializer<Object>) template.getValueSerializer()).setObjectMapper(mapper);
template.afterPropertiesSet();
return template;
}
为什么存进去是JSON但取出来变成LinkedHashMap
这是最常踩的坑:你以为get("key")能自动还原成User,结果拿到的是LinkedHashMap。根本原因是泛型擦除 + 序列化器没绑定具体类型。
-
Jackson2JsonRedisSerializer构造时传Object.class只是占位,不等于“万能反序列化” - RedisTemplate内部不会记住你存的是什么类,它只按你配置的序列化器来编解码
- 必须在每次读操作时显式传入目标类型:
redisTemplate.opsForValue().get("user:1", User.class) - 如果用
BoundValueOperations,也要用带泛型的boundValueOps(key).get(User.class) - 别依赖
@JsonTypeInfo之类注解自动识别——除非你存的时候就写了类型信息,且反序列化器明确启用了它
自定义序列化器要注意的兼容性细节
一旦上线,序列化格式就变成数据契约。改了序列化方式,老数据就可能读不出来。
- 上线前先确认Redis里有没有存量数据;如果有,要么批量迁移,要么保留双序列化逻辑做兼容
- Jackson默认不序列化
null字段,而JDK序列化会存;如果你业务依赖null字段存在,得配mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)并统一认知 - Lombok的
@Data类如果含java.time.LocalDateTime,Jackson默认不认识,必须注册JavaTimeModule,否则存的时候就抛异常 - Spring Boot 2.6+ 默认禁用
ALLOW_COERCION_OF_SCALARS,如果存的是数字但读成字符串,会报错,需要手动启用
序列化不是配完就完的事,它绑定了数据格式、读写逻辑、升级路径。哪怕只是换JSON,也要盯着ObjectMapper的每个开关、每次get()调用的类型参数、还有Redis里真实存的是什么字节。










