当前位置: 首页 > 后端技术 > Java

JDK序列化,遇到serialVersionUID不一致,如何处理?

时间:2023-04-02 00:24:10 Java

公司有一个系统,有很多子服务和频繁的交互。有一些对象需要共享和传输。它们通过JDK序列化(JavaObjectSerialization)进行交互;然而,由于一些难以描述的历史原因,这些对象有多个版本。各版本属性不一致,未设置serialVersionUID。最近一段时间一直在整理/统一代码,打算统一这些对象的版本,修复serialVersionUID。但是由于服务较多,版本上线时会有新旧版本共存的一段时间,所以必须考虑这些对象的序列化兼容性。问题,新对象反序列化必须兼容旧对象Java对象序列化Java对象序列化(Serialization)是指将Java中的对象转换成字节流,以便于在网络中存储或传输,反序列化(反序列化)是指将字节流转置为Java对象。一般来说,Java对象序列化是指使用JDK自带的函数来序列化/反序列化对象,而不是使用其他的序列化库。序列化JDK序列化需要对象实现java.io.Serializable接口。基本用法如下:Serialization//将今天的日期序列化到一个文件中newDate());s.flush();Deserialization//反序列化文件中的字符串和日期.FileInputStreamin=newFileInputStream("tmp");ObjectInputStreams=newObjectInputStream(in);Stringtoday=(String)s.readObject();Datedate=(Date)s.readObject();serialVersionUIDprivatestaticfinallongserialVersionUID=1L;Java对象序列化会使用对象中的serialVersionUID常量属性作为对象的版本号。反序列化的时候会检查版本号是否一致。如果不一致,则序列化失败,默认抛出InvalidClassException。一般情况下,JVM会为每个实现了Serializable接口的类生成一个serialVersionUID(long)。这个ID的计算规则是根据当前的类信息(类名、属性等)生成的,所以当属性发生变化时,serialVersionUID这个serialVersionUID的生成也会发生变化,这和使用的JDK有关。不同的JDK可能会生成不同的版本号,所以最好手动生成一个。大多数JAVAIDE都会提供这个生成功能,考虑到在实际业务场景中,更改属性是很常见的。如果使用自动生成的版本号,容易造成serialVersionUID不一致,导致反序列化失败。serialVersionUID不一致时的兼容性处理也很简单。既然反序列化使用ObjectInputStream来实现,那么这里自定义一个CompatibleInputStream来继承ObjectInputStream,然后重写readClassDescriptor方法。当目标数据Class版本号与本地Class版本号不一致时,本地版本ClasspublicclassCompatibleInputStreamextendsObjectInputStream{privatestaticLoggerlogger=LoggerFactory.getLogger(CompatibleInputStream.class);publicCompatibleInputStream(InputStreamin)throwsIOException{super(in);}@OverrideprotectedObjectStreamClassIreadClassDescriptor()throwsOException,ClassNotFoundException{ObjectStreamClassresultClassDescriptor=super.readClassDescriptor();//最初流描述符ClasslocalClass;//此描述符代表的本地JVM中的类。尝试{localClass=Class.forName(resultClassDescriptor.getName());}catch(ClassNotFoundExceptione){logger.error(“没有本地类”+resultClassDescriptor.getName(),e);返回结果类描述符;}ObjectStreamClasslocalClassDescriptor=ObjectStreamClass.lookup(localClass);if(localClassDescriptor!=null){//仅当类实现可序列化finallonglocalSUID=localClassDescriptor.getSerialVersionUID();finallongstreamSUID=resultClassDescriptor.getSerialVersionUID();if(streamSUID!=localSUID){//检查serialVersionUID不匹配。finalStringBuffers=newStringBuffer("重写序列化类版本不匹配:");s.append("localserialVersionUID=").append(localSUID);s.append("streamserialVersionUID=").append(streamSUID);Exceptione=newInvalidClassException(s.toString());logger.error("可能是致命的反序列化操作。",e);resultClassDescriptor=localClassDescriptor;//使用本地类描述符进行反序列化}}返回resultClassDescriptor;}}以上关键字代码摘自https://stackoverflow.com/a/1...使用方式://反序列化文件中的字符串和日期.FileInputStreamin=newFileInputStream("tmp");//反序列化使用上面的CompatibleInputStream即可ObjectInputStreams=newCompatibleInputStream(in);Stringtoday=(String)s.readObject();Datedate=(Date)s.readObject();