Redis存储对象类名变更后反序列化失败的解决方案问询
解决Redis反序列化类名变更后的SerializationException问题
这个问题我之前也碰到过,核心原因是Redis默认使用的JDK序列化机制,会把类的全限定名硬编码在序列化的字节数据里。你把类名从StorageConnection改成DataConnection后,反序列化时JVM找不到旧类名对应的类,自然就抛出SerializationException了。好在你说字段和serialVersionUID都没变,有两个靠谱的解决技巧:
技巧1:自定义序列化器,映射新旧类名
通过重写JDK序列化器的类解析逻辑,把旧类名替换成新类名,这样就能兼容旧数据的反序列化了。
先写一个自定义的序列化器:
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; public class CustomJdkSerializer extends JdkSerializationRedisSerializer { @Override protected Object deserialize(byte[] bytes) { try (ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes); ObjectInputStream objectStream = new ObjectInputStream(byteStream) { @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { // 把旧类名替换成新类名,注意替换成你实际的包路径 if ("com.yourpackage.StorageConnection".equals(desc.getName())) { return DataConnection.class; } return super.resolveClass(desc); } }) { return objectStream.readObject(); } catch (Exception ex) { throw new SerializationException("Could not deserialize object", ex); } } }
然后把这个序列化器配置到你的RedisTemplate里:
// 在AdminDataBaseServiceImpl的构造方法或者配置类中设置 this.redisTemplateAdmin.setValueSerializer(new CustomJdkSerializer()); this.redisTemplateAdmin.setHashValueSerializer(new CustomJdkSerializer()); this.redisTemplateAdmin.afterPropertiesSet();
这样修改后,不管是旧数据还是新存的数据,都能正常反序列化成DataConnection对象。
技巧2:一次性迁移旧数据
如果你的Redis数据量不大,或者不需要长期兼容旧类名,可以一次性把旧数据读取出来,转换成新类再存回去。
写一段临时执行的迁移代码:
import org.springframework.beans.BeanUtils; // 注意:这段代码只需要执行一次,完成后就可以删掉 public void migrateOldData(String uniqueKey) { // 先用旧类读取数据 StorageConnection oldConn = (StorageConnection) redisTemplateAdmin.opsForValue().get(uniqueKey); if (oldConn != null) { // 转换成新类,因为字段完全一致,可以用BeanUtils拷贝 DataConnection newConn = new DataConnection(); BeanUtils.copyProperties(oldConn, newConn); // 重新存入Redis redisTemplateAdmin.opsForValue().set(uniqueKey, newConn); } }
执行完这段代码后,所有旧数据都会被替换成新类的序列化数据,之后就可以用正常的反序列化逻辑了。
重要提醒
一定要确保StorageConnection和DataConnection的serialVersionUID完全相同,你已经提到UID保持不变,这点刚好满足,不然即使字段一样,也会因为UID不匹配抛出序列化异常。
内容的提问来源于stack exchange,提问作者Abhishek
相关产品推荐
相关产品推荐

