java – 当对象具有不同的serialVersionUID时,如何反序列化一个对象的数据库

前端之家收集整理的这篇文章主要介绍了java – 当对象具有不同的serialVersionUID时,如何反序列化一个对象的数据库前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我的客户端有一个oracle数据库,一个对象通过objOutStream.writeObject作为一个blob字段被持久化,该对象现在有一个不同的serialVersionUID(即使该对象没有变化,也许是不同的jvm版本),当他们尝试去序列化抛出异常:
  1. java.io.InvalidClassException: CommissionResult; local class incompatible:
  2. stream classdesc serialVersionUID = 8452040881660460728,local class serialVersionUID = -5239021592691549158

他们没有为serialVersionUID分配一个固定的值,因为从现在开始,有些事情改变了这个异常被抛出.现在他们不想松开任何数据,要做到这一点,我认为最好的是读取对象,对它们进行序列化,并通过XMLEncoder再次保持它们,以避免像当前的“类不兼容”错误那样的未来错误.

显然,对于该对象,serialVersionUID持续存在2个不同的值,因此我想要读取数据,尝试使用一个值,如果失败,则尝试使用其他值.为此,我尝试更改该类的serialVersionUID运用
the ASM api.我已经能够更改值,但是问题是如何使类激活更改,所以当它被反序列化时,objInpStr.readObject()将我的特定serializedVersionUID的修改版本.我做了一个测试类来模拟真正的环境,我拿一个对象(其具有与对象具有不同的serialVersionUID问题的对象)对象名称是Reservation的属性
CommissionResult:

  1. public class Reservation implements java.io.Serializable {
  2.  
  3.  
  4. private CommissionResult commissionResult = null;
  5.  
  6. }
  7.  
  8.  
  9. public class CommissionResult implements java.io.Serializable{
  10.  
  11.  
  12.  
  13. }
  14.  
  15.  
  16. import org.objectweb.asm.ClassReader;
  17. import org.objectweb.asm.ClassVisitor;
  18. import org.objectweb.asm.ClassWriter;
  19. import org.objectweb.asm.commons.SerialVersionUIDAdder;
  20.  
  21. public class SerialVersionUIDRedefiner extends ClassLoader {
  22.  
  23.  
  24. public void workWithFiles() {
  25. try {
  26. Reservation res = new Reservation();
  27. FileOutputStream f = new FileOutputStream("/home/xabstract/tempo/res.ser");
  28. ObjectOutputStream out = new ObjectOutputStream(f);
  29.  
  30. out.writeObject(res);
  31.  
  32. out.flush();
  33. out.close();
  34.  
  35. ClassWriter cw = new ClassWriter(0);
  36. ClassVisitor sv = new SerialVersionUIDAdder(cw); //assigns a real serialVersionUID
  37. ClassVisitor ca = new MyOwnClassAdapter(sv); //asigns my specific serialVerionUID value
  38. ClassReader cr=new ClassReader("Reservation");
  39. cr.accept(ca,0);
  40.  
  41. SerialVersionUIDRedefiner loader= new SerialVersionUIDRedefiner();
  42. byte[] code = cw.toByteArray();
  43. Class exampleClass = loader.defineClass("Reservation",code,code.length); //at this point the class Reservation has an especific serialVersionUID value that I put with MyOwnClassAdapter
  44.  
  45. loader.resolveClass(exampleClass);
  46. loader.loadClass("Reservation");
  47. DeserializerThread dt=new DeserializerThread();
  48. dt.setContextClassLoader(loader);
  49. dt.run();
  50. } catch (Exception e) {
  51. e.printStackTrace();
  52. }}
  53.  
  54.  
  55.  
  56. import java.io.FileInputStream;
  57. import java.io.ObjectInputStream;
  58.  
  59. public class DeserializerThread extends Thread {
  60.  
  61. public void run() {
  62. try {
  63. FileInputStream f2;
  64.  
  65. f2 = new FileInputStream("/home/xabstract/tempo/res.ser");
  66.  
  67. ObjectInputStream in = new ObjectInputStream(f2);
  68.  
  69.  
  70. Reservation c1 = (Reservation)in.readObject();
  71.  
  72.  
  73.  
  74. System.out.println(c1);
  75.  
  76. } catch (Exception e) {
  77.  
  78. e.printStackTrace();
  79. }
  80. stop();
  81. }
  82. }
  83.  
  84. MyOwnClassAdapter Relevant code:
  85.  
  86.  
  87.  
  88. public void visitEnd() {
  89. // asign SVUID and add it to the class
  90.  
  91. try {
  92.  
  93. cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,"serialVersionUID","J",null,new Long(-11001));//computeSVUID()));
  94. } catch (Throwable e) {
  95. e.printStackTrace();
  96. throw new RuntimeException("Error while computing SVUID for x",e);
  97. }
  98.  
  99.  
  100. super.visitEnd();
  101. }

测试应该失败,并且java.io.InvalidClassException“本地类不兼容”
因为我在保存文件之后更改了serialVersionUID,并使用了一个新的文件
de文件,但它不会失败,所以这意味着ObjectInputStream.readObject不是
使用我的修改版本的Reservation类.

有任何想法吗?提前致谢.

!!!!!!!!!!!!!更新:

好的,可以重新定义resultClassDescriptor来覆盖流serialVersionUID,但是有些奇怪的事情发生,正如我之前所说的那样
是类的2个版本持久化,对象与serialVersionUID = -5239021592691549158L和其他值为8452040881660460728L这最后一个值是
如果我没有为本地类指定值,则生成该值.

– 如果我没有为serialVersionUID指定一个值,则使用默认值(8452040881660460728L),但是不可能对对象进行解除删除
有另一个值,抛出一个错误,说属性是另一个类型.

– 如果我指定值-5239021592691549158L,则类持久化该值
已成功解除序列化,但不是其他类型,同样的错误类型.

这是错误跟踪:

潜在的致命反序列化操作. java.io.InvalidClassException:覆盖序列化类版本不匹配:local serialVersionUID = -5239021592691549158 stream serialVersionUID = 8452040881660460728
java.lang.ClassCastException:不能将java.util.HashMap的实例分配给com.posadas.ic.rules.common.commisionRules.CommissionResult.statusCode,类型为java.lang.String,例如com.posadas.ic.rules. common.commisionRules.CommissionResult

抛出此错误时,类的值为-5239021592691549158,如果更改
该值是8452040881660460728该类成功解除序列化,那么会发生什么?为什么这个错误试图为错误的类投射?

谢谢

解决方法

豪尔赫我在 http://forums.sun.com/thread.jspa?threadID=518416发现了一个解决方案.

在你的项目中创建下面的类.在创建ObjectInputStream对象的时候,使用DecompressibleInputStream代替它,它使用新版本的Id类反序列化旧对象.

  1. public class DecompressibleInputStream extends ObjectInputStream {
  2.  
  3. public DecompressibleInputStream(InputStream in) throws IOException {
  4. super(in);
  5. }
  6.  
  7.  
  8. protected ObjectStreamClass readClassDescriptor() throws IOException,ClassNotFoundException {
  9. ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); // initially streams descriptor
  10. Class localClass = Class.forName(resultClassDescriptor.getName()); // the class in the local JVM that this descriptor represents.
  11. if (localClass == null) {
  12. System.out.println("No local class for " + resultClassDescriptor.getName());
  13. return resultClassDescriptor;
  14. }
  15. ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
  16. if (localClassDescriptor != null) { // only if class implements serializable
  17. final long localSUID = localClassDescriptor.getSerialVersionUID();
  18. final long streamSUID = resultClassDescriptor.getSerialVersionUID();
  19. if (streamSUID != localSUID) { // check for serialVersionUID mismatch.
  20. final StringBuffer s = new StringBuffer("Overriding serialized class version mismatch: ");
  21. s.append("local serialVersionUID = ").append(localSUID);
  22. s.append(" stream serialVersionUID = ").append(streamSUID);
  23. Exception e = new InvalidClassException(s.toString());
  24. System.out.println("Potentially Fatal Deserialization Operation. " + e);
  25. resultClassDescriptor = localClassDescriptor; // Use local class descriptor for deserialization
  26. }
  27. }
  28. return resultClassDescriptor;
  29. }
  30. }

猜你在找的Java相关文章