导读:本期聚焦于郭世昌创作的《如何在Spring Boot中整合Redis并实现可读的对象序列化存储?》,敬请观看详情。把对象直接放进Redis,最省事的方法是依赖JDK序列化,但存进去的值会变成一串类似\xAC\xED的十六进制乱码,不仅在Redis客户端里难以排查,还会让数据体积膨胀、跨语言读取基本不可行。要解决这个问题,通常需要把对象转成JSON或类似的结构化文本再写入,同时保留反序列化所需的类型信息。本文围绕Spring Boot与Redis的整合过程,对比JDK序列化、JSON序列化方案的差异,重点演示GenericJackson2JsonRedisSerializer的配置方式,说明如何让对象以带类型提示的JSON格式存储,并处理LocalDateTime等Java 8时间类型的序列化兼容问题。文章还会给出完整的配置类、实体类以及读写示例,帮助你在项目中快速落地可读、可维护的Redis对象缓存方案。

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

如何在Spring Boot中整合Redis并实现可读的对象序列化存储?

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

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0917/58335.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。