如何在Redis中作为值存储和读取可序列化的Java对象
Redis中存储和读取可序列化Java对象的实现方案
核心问题说明
你遇到的读取报错、字节损坏异常的核心原因有两个:
- JDK序列化要求存储的对象必须实现
Serializable接口,否则序列化、反序列化都会失败 - 直接将Redis返回的结果转字符串再转字节的操作会损坏数据:序列化后的字节不属于标准UTF-8字符集,转字符串过程中会丢弃无法识别的字节,导致最终拿到的字节数组和原始序列化字节不一致
实现步骤
1 改造序列化实体类
需要存储的Java类必须显式实现Serializable接口,同时指定固定的serialVersionUID,避免类结构修改后反序列化不兼容:
import java.io.Serializable; public class SomeObject implements Serializable { // 固定序列化ID,只要类的兼容字段不修改就可以正常反序列化 private static final long serialVersionUID = 1L; // 类的自有属性 private String id; private String name; // 不需要序列化的属性加transient关键字修饰 private transient String tempField; // getter、setter省略 }
2 配置Redis客户端序列化规则
如果使用Spring Data Redis,需要将value的序列化器设置为字节数组/JDK序列化器,不要使用默认的String序列化器,避免字节编码损坏:
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.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // key使用String序列化器,方便直接查看 template.setKeySerializer(new StringRedisSerializer()); // value使用JDK序列化器,直接操作字节数组 template.setValueSerializer(new JdkSerializationRedisSerializer()); template.afterPropertiesSet(); return template; } }
3 修正后的存取方法
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.time.Duration; // 存储对象到Redis public boolean addToRedis(Object obj) { // 提前校验对象是否支持序列化 if (!(obj instanceof Serializable)) { throw new IllegalArgumentException("待存储对象必须实现Serializable接口"); } // 使用try-with-resources自动关闭流 try (ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(out)) { os.writeObject(obj); // 字符串统一使用双引号,key可以根据业务调整 redis.set("ObjectKey", out.toByteArray(), Duration.ofSeconds(5000)); return true; } catch (IOException e) { e.printStackTrace(); return false; } } // 从Redis读取对象 public SomeObject getObjectFromRedis() { // 直接获取原始字节数组,不要做任何字符串转换 byte[] objBytes = redis.get("ObjectKey"); if (objBytes == null || objBytes.length == 0) { return null; } try (ByteArrayInputStream in = new ByteArrayInputStream(objBytes); ObjectInputStream is = new ObjectInputStream(in)) { return (SomeObject) is.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } }
常见异常排查
- 反序列化报
InvalidClassException:检查serialVersionUID是否一致,类的包路径、类名是否和序列化时完全相同 - 字节损坏异常:检查Redis客户端的value序列化器是否为JdkSerializationRedisSerializer,读取时是否直接拿字节数组没有转字符串
- 序列化时报
NotSerializableException:检查类的所有非transient属性是否都支持序列化,实体类是否实现了Serializable接口
内容的提问来源于stack exchange,提问作者Nitesh Nandan
相关产品推荐
相关产品推荐

