Spring Boot从Java 8迁移至Java 17后无法反序列化Redis数据
问题分析
从报错信息里的字节内容(sr/com.ceb.shl.shlonline.model.content.Assessments)可以直接看出:Redis中存储的数据是Java原生序列化格式,而你当前Java17应用使用的GenericJackson2JsonRedisSerializer是JSON序列化器,两者格式不兼容,导致JSON解析失败。
原因是原Java8应用大概率使用了默认的JdkSerializationRedisSerializer写入数据,你迁移后更换了序列化方式,自然无法读取旧数据。
解决方案
提供两种可行方案,按需选择:
方案1:兼容新旧数据(推荐渐进式过渡)
自定义混合序列化器,优先用JSON解析新数据,失败则用JDK序列化器解析旧数据,同时新写入的数据统一用JSON格式:
- 实现混合序列化器:
public class HybridRedisSerializer implements RedisSerializer<Object> { private final GenericJackson2JsonRedisSerializer jsonSerializer; private final JdkSerializationRedisSerializer jdkSerializer; public HybridRedisSerializer(ObjectMapper objectMapper) { this.jsonSerializer = new GenericJackson2JsonRedisSerializer(objectMapper); this.jdkSerializer = new JdkSerializationRedisSerializer(); } @Override public byte[] serialize(Object o) throws SerializationException { // 新数据统一用JSON序列化 return jsonSerializer.serialize(o); } @Override public Object deserialize(byte[] bytes) throws SerializationException { if (bytes == null || bytes.length == 0) { return null; } try { // 先尝试JSON解析 return jsonSerializer.deserialize(bytes); } catch (Exception e) { // JSON解析失败,降级用JDK序列化解析旧数据 return jdkSerializer.deserialize(bytes); } } }
- 修改RedisTemplate配置,替换为混合序列化器:
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); ObjectMapper objectMapper = objectMapper(); HybridRedisSerializer hybridSerializer = new HybridRedisSerializer(objectMapper); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(hybridSerializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(hybridSerializer); return redisTemplate; } // 保留原ObjectMapper配置 @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); return mapper; }
方案2:全量迁移旧数据到JSON格式
如果允许短暂停服或批量处理,可一次性将旧数据转换为JSON格式:
- 临时用JDK序列化器读取所有旧数据,再用JSON序列化器重新写入:
// 临时执行代码,完成后可删除 RedisTemplate<String, Object> tempRedisTemplate = new RedisTemplate<>(); tempRedisTemplate.setConnectionFactory(redisConnectionFactory); tempRedisTemplate.setKeySerializer(new StringRedisSerializer()); tempRedisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); tempRedisTemplate.setHashKeySerializer(new StringRedisSerializer()); tempRedisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); tempRedisTemplate.afterPropertiesSet(); // 读取目标哈希表的所有数据 HashOperations<String, String, Object> tempHashOps = tempRedisTemplate.opsForHash(); String cacheKey = cacheKeys.getCacheAssessmentKey(); Map<String, Object> allOldData = tempHashOps.entries(cacheKey); // 用你的新RedisTemplate重新写入(自动转为JSON格式) yourNewRedisTemplate.opsForHash().putAll(cacheKey, allOldData);
- 数据迁移完成后,恢复原有的JSON序列化配置即可。
额外注意事项
- 若使用方案1,需确保
Assessments类在Java17中可正常序列化:添加serialVersionUID,避免类结构变化导致的反序列化失败:
public class Assessments implements Serializable { private static final long serialVersionUID = 1L; // 类属性与方法 }
内容的提问来源于stack exchange,提问作者Shashank Garg
相关产品推荐
相关产品推荐

