- 存入 Redis:将 Java 对象转换为字节流(序列化),然后将这个字节流作为
value存储到 Redis 的String或Hash等结构中。 - 从 Redis 取出:从 Redis 中获取字节流,然后将这个字节流转换回原来的 Java 对象(反序列化)。
下面我将从 基本原理、常用序列化方式、完整代码示例、最佳实践 四个方面进行详细说明。

基本原理
Java 序列化是将 Java 对象的状态信息转换为可以存储或传输的形式(通常是字节流)的过程,这个过程需要对象实现 java.io.Serializable 接口,反序列化则是相反的过程,将字节流恢复成 Java 对象。
当使用 Redis 客户端(如 Jedis, Lettuce)操作 Redis 时,客户端会自动处理序列化和反序列化,我们通常需要配置一个 Serializer 接口的实现,告诉客户端如何进行转换。
org.springframework.data.redis.serializer 包中定义了几个常用的序列化器:
JdkSerializationRedisSerializer: 使用 Java 原生的序列化机制,优点是无需额外依赖,缺点是序列化后体积大、可读性差,且可能存在安全问题。StringRedisSerializer: 将String类型的key和value进行序列化,它实际上是UTF-8编码的转换器,最常用,最安全。GenericJackson2JsonRedisSerializer: 使用 Jackson 库将对象序列化为 JSON 字符串,优点是可读性好,跨语言,体积相对较小,是目前最推荐的方案之一。OxmSerializer: 使用 XML 进行序列化,较少使用。
常用序列化方式对比
| 序列化方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| JDK 原生 | 无需额外依赖 Java 生态内兼容性好 |
序列化后体积大 可读性差(二进制) 性能相对较低 跨版本可能不兼容 |
项目内部使用,对性能和体积要求不高的场景。 |
| JSON | 可读性好,便于调试 跨语言,通用性强 序列化后体积相对较小 |
需要引入 Jackson/Gson 等依赖 无法直接序列化复杂对象(如循环引用) 反序列化时需要目标类的无参构造器 |
强烈推荐,大多数 Web 应用、微服务场景的首选。 |
| String | 性能极高 可读性最好 兼容性最强 |
只能处理 String 类型 |
存储 key 或简单的 value,如 session:123。 |
| Protostuff/Kryo | 序列化速度极快,体积极小 高性能序列化框架 |
需要额外依赖 可读性差 |
对性能和存储空间有极致要求的场景,如缓存大量数据。 |
完整代码示例 (以 Spring Boot + Redis + Jackson 为例)
这是目前最主流和推荐的方案,我们将使用 GenericJackson2JsonRedisSerializer。

步骤 1: 项目依赖 (pom.xml)
确保你的 pom.xml 中包含 Spring Boot Data Redis 和 Jackson 依赖。
<dependencies>
<!-- Spring Boot Starter Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Lombok (可选,简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Boot Web (可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
步骤 2: 配置 Redis 连接 (application.yml)
spring:
redis:
host: 127.0.0.1
port: 6379
# password: yourpassword # 如果有密码
database: 0
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
步骤 3: 配置序列化器
创建一个配置类,明确指定 key 和 value 的序列化方式。
key使用StringRedisSerializer,保证可读性。value使用GenericJackson2JsonRedisSerializer,支持复杂对象。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 1. 创建 RedisTemplate 对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 2. 创建序列化器
// Key 使用 String 序列化
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// Value 使用 JSON 序列化
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
// 3. 设置 Key 和 Value 的序列化器
template.setKeySerializer(stringSerializer);
template.setValueSerializer(jsonSerializer);
template.setHashKeySerializer(stringSerializer);
template.setHashValueSerializer(jsonSerializer);
// 4. 初始化 RedisTemplate
template.afterPropertiesSet();
return template;
}
}
步骤 4: 创建可序列化的 Java 对象
要被序列化的类 必须有无参构造函数,并且最好实现 Serializable 接口(虽然 Jackson 不强制,但这是一个好习惯)。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor // Jackson 反序列化需要
@AllArgsConstructor
public class User implements Serializable {
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
}
步骤 5: 编写 Service 进行操作
注入 RedisTemplate 并使用它来存取对象。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String USER_KEY_PREFIX = "user:";
/**
* 保存用户信息到 Redis
* @param user 用户对象
*/
public void saveUser(User user) {
String key = USER_KEY_PREFIX + user.getId();
// opsForValue().set 会自动调用配置的 GenericJackson2JsonRedisSerializer 进行序列化
redisTemplate.opsForValue().set(key, user, 10, TimeUnit.MINUTES);
System.out.println("用户信息已存入 Redis: " + key);
}
/**
* 从 Redis 获取用户信息
* @param id 用户ID
* @return 用户对象,如果不存在则返回 null
*/
public User getUser(Long id) {
String key = USER_KEY_PREFIX + id;
// get 会自动调用配置的 GenericJackson2JsonRedisSerializer 进行反序列化
Object obj = redisTemplate.opsForValue().get(key);
if (obj instanceof User) {
return (User) obj;
}
return null;
}
}
步骤 6: 测试
你可以写一个单元测试或 Controller 来调用 UserService 的方法。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testSaveAndGetUser() {
User user = new User(1L, "张三", "zhangsan@example.com", LocalDateTime.now());
// 保存
userService.saveUser(user);
// 获取
User retrievedUser = userService.getUser(1L);
System.out.println("从 Redis 取出的用户: " + retrievedUser);
// 验证
assert user.getId().equals(retrievedUser.getId());
assert user.getUsername().equals(retrievedUser.getUsername());
}
}
运行结果分析: 当你用 Redis CLI 连接到 Redis 并查看数据时,你会看到类似这样的内容(可读的 JSON 格式):
0.0.1:6379> get user:1
"{\"@class\":\"com.example.demo.User\",\"id\":1,\"username\":\"张三\",\"email\":\"zhangsan@example.com\",\"createTime\":\"2025-10-27T10:30:00\"}"
注意 @class 字段,这是 Jackson2JsonRedisSerializer 添加的,它记录了原始对象的类全限定名,这样在反序列化时,Jackson 就能知道应该将 JSON 数据转换回哪个类的实例。
最佳实践与注意事项
-
选择合适的序列化器:
- 优先选择
GenericJackson2JsonRedisSerializer:它在可读性、性能和功能之间取得了很好的平衡。 key始终使用StringRedisSerializer,保证key的可读性和通用性。- 避免直接使用
JdkSerializationRedisSerializer,除非你有特殊原因。
- 优先选择
-
处理缓存穿透、击穿、雪崩:
- 穿透:查询一个不存在的数据,解决方案:缓存空对象(
null)或使用布隆过滤器。 - 击穿:一个热点key突然失效,大量请求直接打到数据库,解决方案:设置合理的过期时间,使用互斥锁(如 Redis 的
SETNX)。 - 雪崩:大量key在同一时间集体失效,解决方案:给key的过期时间加上随机值。
- 穿透:查询一个不存在的数据,解决方案:缓存空对象(
-
Redis 数据结构选择:
- 存储单个对象:使用
String结构,key是唯一标识,value是序列化后的对象。 - 存储对象列表:可以使用
List结构,将多个对象序列化后存入一个列表。 - 存储对象集合:可以使用
Set结构。 - 存储对象属性:如果对象很大但只需要更新部分属性,可以考虑使用
Hash结构,field是属性名,value是属性值。
- 存储单个对象:使用
-
反序列化时的类型安全:
redisTemplate.getForObject()方法提供了更直接的类型转换,可以避免手动instanceof判断。
// 替代方案
public User getUserWithGetForObject(Long id) {
String key = USER_KEY_PREFIX + id;
// 直接指定返回类型,RedisTemplate 会自动处理类型转换
return redisTemplate.opsForValue().get(key, User.class);
}
- 版本兼容性:
如果你使用
JdkSerializationRedisSerializer,要特别注意应用版本升级时,Java 类的serialVersionUID变化会导致反序列化失败,使用 JSON 序列化可以避免这个问题。
