Spring Boot 集成 Redis 乱码问题详解:原因分析与解决方案
在 Spring Boot 集成 Redis 时,若使用默认配置的 RedisTemplate 操作字符串,常出现 key/value 乱码(如前缀含 \xac\xed\x00\x05t\x00\x03 等特殊字符)。核心原因是 RedisTemplate 默认采用 JdkSerializationRedisSerializer 序列化方式(基于 JDK 原生序列化,会生成二进制数据),而 StringRedisTemplate 或自定义序列化配置可彻底解决该问题。从 “乱码原因→两种解决方案→序列化对比→实战示例” 四个维度,系统讲解 Redis 乱码的解决方法。
乱码的根本原因:默认序列化方式不匹配
首先需明确:Redis 本身是 二进制安全的键值存储,不限制数据格式,但 Spring Boot 提供的 RedisTemplate 默认序列化方式会导致 “人类不可读” 的二进制数据,被误认为 “乱码”。
1. RedisTemplate 默认序列化配置
RedisTemplate 对不同类型的序列化器默认配置如下:
| 数据类型 | 默认序列化器 | 序列化结果特点 |
|---|---|---|
| Key(键) | JdkSerializationRedisSerializer |
生成二进制数据,前缀含特殊字符(如 \xac\xed),乱码 |
| Value(值) | JdkSerializationRedisSerializer |
同上,二进制数据,不可读 |
| Hash Key | JdkSerializationRedisSerializer |
同上 |
| Hash Value | JdkSerializationRedisSerializer |
同上 |
问题根源:JdkSerializationRedisSerializer 的特性
- 原理:基于 JDK
ObjectOutputStream实现序列化,要求被序列化的对象实现Serializable接口; - 结果:序列化后的数据是 二进制字节数组,存储到 Redis 后显示为乱码(如
\xac\xed\x00\x05t\x00\x03key); - 场景不匹配:若仅操作字符串(如
String类型的 key/value),二进制序列化完全没必要,且导致乱码。
2. 示例:默认配置的乱码效果
使用默认 RedisTemplate 存储字符串:
1 |
|
在 Redis 客户端(如 redis-cli)查看结果:
1 | 127.0.0.1:6379> get "\xac\xed\x00\x05t\x00\x04name" # key 乱码 |
可见 key 和 value 均含 \xac\xed 等特殊字符,完全不可读 —— 这就是典型的默认序列化导致的乱码问题。
解决方案一:直接使用 StringRedisTemplate(推荐字符串场景)
StringRedisTemplate 是 RedisTemplate 的子类,专门为 String 类型 设计,默认采用 StringRedisSerializer(字符串序列化,UTF-8 编码),完美解决乱码问题,且无需额外配置。
1. StringRedisTemplate 的核心特性
| 数据类型 | 序列化器 | 序列化结果特点 |
|---|---|---|
| Key(键) | StringRedisSerializer |
纯字符串,UTF-8 编码,无乱码 |
| Value(值) | StringRedisSerializer |
同上 |
| Hash Key | StringRedisSerializer |
同上 |
| Hash Value | StringRedisSerializer |
同上 |
优势:
- 零配置:直接注入即可使用,无需自定义 Bean;
- 无乱码:序列化结果为人类可读的字符串,与
redis-cli操作结果一致; - 轻量级:仅处理 String 类型,性能优于
RedisTemplate的通用序列化。
2. 实战示例:使用 StringRedisTemplate
(1)注入 StringRedisTemplate
1 | import org.springframework.beans.factory.annotation.Autowired; |
(2)验证结果
调用接口存储数据:
http://localhost:8080/redis/set/name/张三;在redis-cli查看:
1
2127.0.0.1:6379> get name # key 无乱码
"张三" # value 无乱码
适用场景:
- 仅操作 String 类型的 key/value(如缓存用户 Token、验证码、简单字符串数据);
- 需与
redis-cli或其他语言客户端(如 Python/Go 的 Redis 客户端)交互,要求数据可读。
解决方案二:自定义 RedisTemplate 序列化(支持复杂对象)
若需存储 复杂对象(如 User、Order 实体类),StringRedisTemplate 无法直接使用(需手动转 JSON),此时需自定义 RedisTemplate 的序列化器 —— 将 Key 用 StringRedisSerializer(避免乱码),Value 用 Jackson2JsonRedisSerializer(JSON 序列化,兼顾可读性和对象转换)。
1. 核心序列化器选择
(1)Key 序列化:StringRedisSerializer
- 原因:Key 通常是简单字符串(如
user:1001),用字符串序列化可确保无乱码,且与其他客户端兼容; - 编码:UTF-8,支持中文 key(如
用户:1001)。
(2)Value 序列化:Jackson2JsonRedisSerializer
- 原因:JSON 序列化的优势:
- 可读性强:序列化结果为 JSON 字符串,
redis-cli可直接查看; - 无需实现
Serializable:避免 JDK 序列化的接口强制要求; - 跨语言兼容:JSON 是通用格式,其他语言客户端可解析;
- 可读性强:序列化结果为 JSON 字符串,
- 依赖:需引入 Jackson 依赖(Spring Boot Web 依赖已包含,无需额外添加)。
2. 自定义 RedisTemplate 配置(完整代码)
1 | import com.fasterxml.jackson.annotation.JsonAutoDetect; |
关键优化点:
- 日期序列化:通过
JavaTimeModule支持 JDK 8 的LocalDateTime/LocalDate,避免默认时间戳格式(可读性差); - Hash 序列化:同时配置 Hash Key 和 Hash Value 的序列化,解决 Hash 类型数据的乱码;
afterPropertiesSet():初始化方法,确保序列化器配置生效(不可省略)。
3. 实战示例:存储复杂对象
(1)定义实体类(无需实现 Serializable)
1 | import java.time.LocalDateTime; |
(2)使用自定义 RedisTemplate 操作对象
1 | import org.springframework.beans.factory.annotation.Autowired; |
(3)验证结果
调用接口存储对象:
http://localhost:8080/redis/set/user/1001;在redis-cli查看:
1
2127.0.0.1:6379> get "user:1001" # key 无乱码
"{\"id\":1001,\"name\":\"张三\",\"age\":25,\"createTime\":\"2024-05-20T16:30:00\"}" # Value 为 JSON 字符串,无乱码
适用场景:
- 需存储复杂对象(如实体类、集合);
- 需兼顾数据可读性和对象自动转换(无需手动 JSON 序列化 / 反序列化)。
三种序列化方式对比(避免选择误区)
为了更清晰选择序列化方案,对比常见的三种 Redis 序列化器:
| 序列化器 | 核心特点 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| JdkSerializationRedisSerializer(默认) | JDK 原生序列化,二进制数据 | 支持任何实现 Serializable 的对象 | 乱码、不可读、需实现接口、跨语言差 | 仅存储 Java 内部对象,不与其他客户端交互 |
| StringRedisSerializer | 字符串序列化,UTF-8 编码 | 无乱码、可读性强、跨语言兼容、轻量 | 仅支持 String 类型,不支持复杂对象 | 存储 String 类型的 key/value(如 Token、验证码) |
| Jackson2JsonRedisSerializer | JSON 序列化,字符串格式 | 无乱码、可读性强、支持复杂对象、跨语言兼容 | 需配置 Jackson、性能略低于 JDK 序列化 | 存储复杂对象(如实体类),需跨语言交互 |
结论:
- 字符串场景:优先用
StringRedisTemplate(StringRedisSerializer); - 复杂对象场景:用自定义
RedisTemplate(Key 用StringRedisSerializer,Value 用Jackson2JsonRedisSerializer); - 避免用默认
RedisTemplate(JdkSerializationRedisSerializer),除非是纯 Java 内部的二进制对象存储。
常见问题与解决方案
1. 自定义 RedisTemplate 后仍乱码
问题原因:
- 未配置 Hash 序列化(仅配置了 Key/Value,Hash Key/Hash Value 仍用默认 JDK 序列化);
- 忘记调用
redisTemplate.afterPropertiesSet(),配置未生效; - 注入的是默认
RedisTemplate,而非自定义的RedisTemplate(如泛型不匹配)。
解决方案:
- 完整配置 Key/Hash Key/Value/Hash Value 四个序列化器;
- 确保调用
afterPropertiesSet(); - 注入时确认泛型:
@Autowired private RedisTemplate<String, Object> redisTemplate(与自定义 Bean 一致)。
2. LocalDateTime 序列化失败(报 “no serializer found”)
问题原因:
- Jackson 默认不支持 JDK 8 新日期类型(
LocalDateTime/LocalDate),需手动注册JavaTimeModule。
解决方案:
在 ObjectMapper 中添加日期模块配置(已包含在自定义 RedisTemplate 代码中):
1 | objectMapper.registerModule(new JavaTimeModule()); // 支持 JDK 8 日期 |
3. 与 StringRedisTemplate 混用导致的问题
问题描述:
用 RedisTemplate 存储 key="name", value="张三"(JSON 序列化),再用 StringRedisTemplate 获取 key="name",得到 "{\"name\":\"张三\"}"(带引号的 JSON 字符串),而非纯字符串。
原因:
RedisTemplate存储 Value 为 JSON 字符串(如"张三"被序列化为"\"张三\"");StringRedisTemplate读取时按纯字符串解析,保留了 JSON 的引号。
解决方案:
- 同一 key 避免混用两种 Template;
- 若需混用,读取时需手动处理(如用 Jackson 反序列化
StringRedisTemplate获取的结果)。
总结
Spring Boot 集成 Redis 乱码的核心是 “序列化方式不匹配”,解决方案分两类:
- 字符串场景:直接使用
StringRedisTemplate,零配置,无乱码,推荐优先选择; - 复杂对象场景:自定义
RedisTemplate,Key 用StringRedisSerializer(无乱码),Value 用Jackson2JsonRedisSerializer(JSON 序列化,支持对象)