Spring Boot整合Redis时,如果只是使用Spring Data Redis默认的RedisTemplate,读写键值对看似简单,但当值是一个自定义对象时,默认的JDK序列化机制会带来一系列麻烦。存储到Redis中的数据以二进制形式存在,在redis-cli里看到的是类似\xAC\xED\x00\x05sr\x00\x0e...的不可读内容,不仅难以调试,还要求类实现Serializable接口,同时序列化结果体积偏大。要实现对象序列化存储且保持可读性,最通用的做法是让对象以JSON格式写入Redis,并在序列化过程中携带必要的类型信息,这样既能直接在客户端中查看值内容,又能在取出时自动还原为正确的Java对象。

Redis序列化器对比与对象存储的问题
Spring Data Redis为RedisTemplate提供了多种序列化器。默认情况下,RedisTemplate没有指定序列化器时会使用JdkSerializationRedisSerializer,它调用Java原生序列化机制,要求缓存对象实现java.io.Serializable接口。这个方案的问题很明显:除了产生不可读的二进制数据,它还把Java类结构信息写入序列化结果,一旦类名、包路径或序列化版本号发生变化,反序列化就可能失败。此外,JDK序列化的性能通常也不如JSON序列化。
相比之下,JSON序列化器更符合现代开发习惯。Jackson2JsonRedisSerializer可以把对象转换成普通JSON字符串,但它有一个限制:需要在构造时明确指定目标类,否则无法在反序列化时知道具体类型。如果想用一个通用的RedisTemplate<String, Object>来读写多种类型,更适合的是GenericJackson2JsonRedisSerializer。它会在JSON中额外写入@class属性,记录对象的全限定类名,反序列化时依据这个信息还原类型,同时缺少类的场景还可以配置安全类型解析。
Spring Boot整合Redis的核心配置
首先在项目中引入Spring Boot的Redis starter依赖,通常只需要在pom.xml中加入spring-boot-starter-data-redis,并通过application.yml或application.properties配置Redis连接地址。Spring Boot会自动创建RedisConnectionFactory,但RedisTemplate的序列化器还是要自己定制,否则默认值仍然是JDK序列化。
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
StringRedisSerializer stringSerializer = new StringRedisSerializer();
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
上面的配置中,键使用StringRedisSerializer以普通字符串存储,避免键也出现二进制格式。值使用GenericJackson2JsonRedisSerializer,这样写入的对象会以JSON形式保存在Redis中,并带有类型信息。配置完成后,redisTemplate.opsForValue().set(...)和get(...)就能直接读写对象,不再需要手动转换。
对象实体与读写示例
定义一个包含常见字段的实体类,例如用户信息,其中包含Long类型ID、字符串名称和LocalDateTime创建时间。Java 8时间类型在默认Jackson序列化下可能报错,因为LocalDateTime没有标准的JavaBean属性。需要在ObjectMapper中注册JavaTimeModule并禁用WRITE_DATES_AS_TIMESTAMPS,才能把时间序列化为可读的ISO字符串。
public class User implements Serializable {
private Long id;
private String name;
private LocalDateTime createTime;
public User() {
}
public User(Long id, String name, LocalDateTime createTime) {
this.id = id;
this.name = name;
this.createTime = createTime;
}
// 省略getter/setter
}
如果直接使用上面配置的GenericJackson2JsonRedisSerializer,它内部有一个默认的ObjectMapper,但可能没有注册时间模块。为了支持LocalDateTime等类型,可以构造一个自定义的ObjectMapper并传给序列化器。例如在配置类中创建GenericJackson2JsonRedisSerializer时,通过自定义ObjectMapper来满足时间类型的序列化需求。
@Bean
public GenericJackson2JsonRedisSerializer jsonRedisSerializer() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.activateDefaultTyping(
objectMapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
return new GenericJackson2JsonRedisSerializer(objectMapper);
}
然后把这个自定义序列化器注入RedisTemplate配置中。写入时调用:
User user = new User(1L, "张三", LocalDateTime.now());
redisTemplate.opsForValue().set("user:1", user);
Object obj = redisTemplate.opsForValue().get("user:1");
System.out.println(obj.getClass()); // 输出 class com.example.demo.entity.User
读取后得到的对象类型是User,证明反序列化成功。在Redis客户端中查看user:1的值,会看到类似{"@class":"com.example.demo.entity.User","id":1,"name":"张三","createTime":"2025-04-16T10:20:30"}的JSON字符串,可读性比JDK序列化好得多。不过要注意,JSON中的@class字段包含了包路径,如果后期修改包名或类名,旧缓存数据会因找不到类而反序列化失败,需要在设计缓存键和类结构时提前考虑版本兼容。
自定义序列化与常见问题处理
虽然GenericJackson2JsonRedisSerializer很方便,但在实际项目中仍有一些容易踩到的点。首先是类型安全问题。Jackson启用默认类型时,反序列化需要信任JSON中的类名,恶意数据可能触发任意类加载。Spring Data Redis提供的GenericJackson2JsonRedisSerializer默认使用了相对安全的白名单验证器,但如果自己创建ObjectMapper并调用activateDefaultTyping,务必传入PolymorphicTypeValidator,只允许反序列化指定包下的类,避免安全问题。
其次是空值处理。默认情况下,GenericJackson2JsonRedisSerializer写入null值时会存储字符串null,而读取时如果键不存在会返回null,但类型信息可能丢失。如果业务中严格要求区分键不存在和值为null,可以自定义RedisSerializer,或者改用StringRedisTemplate配合手动JSON转换,但代价是失去自动类型还原能力。通常缓存场景建议只缓存非空对象,命中率更高。
另外还要注意不同微服务间的类定义一致性。如果多个服务共享Redis,同一个缓存键可能被不同服务读取,这时序列化时的类路径必须完全一致,否则即使JSON内容相同也无法反序列化。建议将共享DTO抽到公共模块,并统一序列化配置。
最后,如果不想在JSON中暴露类全限定名,也可以使用固定类型的Jackson2JsonRedisSerializer<User>,但那样每一种实体都要创建一个RedisTemplate或手动调用序列化器。一般来说,内部系统更看重开发效率和可读性,使用GenericJackson2JsonRedisSerializer是更合适的选择;对外开放接口或跨语言协作时,则建议使用String序列化加JSON转换,保持数据契约清晰。
Spring BootRedis对象序列化修改时间:2026-09-17 04:06:08