我的客户端有一个 oracle 数据库,并且一个对象通过 objOutStream.writeObject 作为 blob 字段持久化,该对象现在有一个不同的serialVersionUID
(即使对象没有变化,可能是不同的 jvm 版本),当他们尝试反序列化一个抛出异常:
java.io.InvalidClassException: CommissionResult; local class incompatible:
stream classdesc serialVersionUID = 8452040881660460728,
local class serialVersionUID = -5239021592691549158
他们从一开始就没有为它分配一个固定的值,serialVersionUID
所以现在有些东西改变了,抛出了异常。现在他们不想丢失任何数据,为此我认为最好的方法是读取对象,反序列化它们,然后通过 XMLEncoder 再次持久化它们,以避免将来出现诸如当前“类不兼容”错误之类的错误。
显然,该对象的持久化有 2 个不同的值,serialVersionUID
所以我想读取数据,尝试使用一个值,如果失败,则尝试使用另一个值,为此,我尝试serialVersionUID
使用
ASM 接口。我已经能够更改该值,但问题是如何对类进行主动更改,因此当它被反序列化时,objInpStr.readObject()
将我修改后的类的版本与我的特定serializedVersionUID
. 我做了一个测试类来模拟真实环境,我取一个对象(它具有不同serialVersionUID
问题的对象作为属性)对象名称是Reservation
属性
CommissionResult
:
public class Reservation implements java.io.Serializable {
private CommissionResult commissionResult = null;
}
public class CommissionResult implements java.io.Serializable{
}
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.SerialVersionUIDAdder;
public class SerialVersionUIDRedefiner extends ClassLoader {
public void workWithFiles() {
try {
Reservation res = new Reservation();
FileOutputStream f = new FileOutputStream("/home/xabstract/tempo/res.ser");
ObjectOutputStream out = new ObjectOutputStream(f);
out.writeObject(res);
out.flush();
out.close();
ClassWriter cw = new ClassWriter(0);
ClassVisitor sv = new SerialVersionUIDAdder(cw); //assigns a real serialVersionUID
ClassVisitor ca = new MyOwnClassAdapter(sv); //asigns my specific serialVerionUID value
ClassReader cr=new ClassReader("Reservation");
cr.accept(ca, 0);
SerialVersionUIDRedefiner loader= new SerialVersionUIDRedefiner();
byte[] code = cw.toByteArray();
Class exampleClass = loader.defineClass("Reservation", code, 0, code.length); //at this point the class Reservation has an especific serialVersionUID value that I put with MyOwnClassAdapter
loader.resolveClass(exampleClass);
loader.loadClass("Reservation");
DeserializerThread dt=new DeserializerThread();
dt.setContextClassLoader(loader);
dt.run();
} catch (Exception e) {
e.printStackTrace();
}}
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class DeserializerThread extends Thread {
public void run() {
try {
FileInputStream f2;
f2 = new FileInputStream("/home/xabstract/tempo/res.ser");
ObjectInputStream in = new ObjectInputStream(f2);
Reservation c1 = (Reservation)in.readObject();
System.out.println(c1);
} catch (Exception e) {
e.printStackTrace();
}
stop();
}
}
MyOwnClassAdapter Relevant code:
public void visitEnd() {
// asign SVUID and add it to the class
try {
cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
"serialVersionUID",
"J",
null,
new Long(-11001));//computeSVUID()));
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException("Error while computing SVUID for x"
, e);
}
super.visitEnd();
}
测试应该因java.io.InvalidClassException
“本地类不兼容”而失败,因为我serialVersionUID
在保存文件并使用新文件读取 de 文件后更改了它,但它没有失败,因此这意味着ObjectInputStream.readObject
没有使用我修改后的Reservation
类版本。
有任何想法吗?提前致谢。
!!!!!!!!!!!!!更新:
好的,可以重新定义 resultClassDescriptor 以覆盖流 serialVersionUID,但是,发生了一些奇怪的事情,正如我之前所说,似乎有 2 个版本的类持久化,serialVersionUID = -5239021592691549158L 的对象和其他值为 8452040881660460728L 的对象最后value 是如果我没有为本地类指定值时生成的值。
- 如果我没有为 serialVersionUID 指定值,则使用默认值 (8452040881660460728L),但无法解除具有其他值的对象,则会引发错误,指出属性属于其他类型。
- 如果我指定值 -5239021592691549158L,则使用该值持久的类将成功反序列化,但其他类则没有,相同的类型错误。
这是错误跟踪:
可能致命的反序列化操作。java.io.InvalidClassException:覆盖序列化类版本不匹配:本地 serialVersionUID = -5239021592691549158 流 serialVersionUID = 8452040881660460728 java.lang.ClassCastException:无法将 java.util.HashMap 的实例分配给字段 com.posadas.ic.rules.common.commisionRules。 com.posadas.ic.rules.common.commisionRules.CommissionResult 实例中 java.lang.String 类型的 CommissionResult.statusCode
抛出此错误时,该类的值为-5239021592691549158,如果将值更改为8452040881660460728,则该类已成功反序列化,那么会发生什么?为什么那个错误会试图转换为错误的类?
谢谢