序列化和反序列化不实现Serializable的对象

时间:2014-12-31 23:54:29

标签: java serialization jboss deserialization

动机:

为了帮助进行远程调试(Java),能够请求远程服务器通过任意对象发送到本地计算机进行检查非常有用。但是,这意味着远程服务器必须能够序列化在运行时事先不知道的任意java对象。

特别是,我希望能够序列化那些不实现Serializable 的对象。我偶然发现JBossSerialization声称与JBossSerialization一起声明......

  

...您可以序列化未实现Serializable的类

大!甚至更好,我设法找到了the code that supposedly demonstrates how to do this

问题

所以捏the code from schabell.org,我写了一个快速测试来检查我是否可以顺利地序列化和反序列化:

import org.jboss.serial.io.JBossObjectInputStream;
import org.jboss.serial.io.JBossObjectOutputStream;
import java.io.*;

class MyObj {   // Test class which doesn't implement Serializable
    public int x;
    MyObj(int x) {this.x = x;}
}

public class SerializationTest {

    public static void main(String[] args) {
        MyObj obj = new MyObj(1);
        byte[] byteArray = getByteArrayFromObject(obj);            // Try to serialize
        MyObj result = (MyObj) getObjectFromByteArray(byteArray);  // Try to deserialize
        System.out.println(result.x);
    }

    // Code that I pinched from website below (http://www.schabell.org/2009/03/jboss-serialization-simple-example.html):
    public static Object getObjectFromByteArray(byte[] bytes) {
        Object result = null;

        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new JBossObjectInputStream(bais);
            result = ois.readObject();   // ERROR HERE!!!
            ois.close();
        } catch (IOException ioEx) {
            ioEx.printStackTrace();
        } catch (ClassNotFoundException cnfEx) {
            cnfEx.printStackTrace();
        }

        return result;
    }

    public static byte[] getByteArrayFromObject(Object obj) {
        byte[] result = null;

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new JBossObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.flush();
            oos.close();
            baos.close();
            result = baos.toByteArray();
        } catch (IOException ioEx) {
            ioEx.printStackTrace();
        }

        return result;
    }
}

问题是测试失败了。调试表明我只能序列化,但不能反序列化。在第26行调用ois.readObject()是罪魁祸首,并将其作为SerializationException

org.jboss.serial.exception.SerializationException: Could not create instance of MyObj - MyObj
    at org.jboss.serial.classmetamodel.ClassMetaData.newInstance(ClassMetaData.java:342)
    at org.jboss.serial.persister.RegularObjectPersister.readData(RegularObjectPersister.java:239)
    at org.jboss.serial.objectmetamodel.ObjectDescriptorFactory.readObjectDescriptionFromStreaming(ObjectDescriptorFactory.java:412)
    at org.jboss.serial.objectmetamodel.ObjectDescriptorFactory.objectFromDescription(ObjectDescriptorFactory.java:82)
    at org.jboss.serial.objectmetamodel.DataContainer$DataContainerDirectInput.readObject(DataContainer.java:643)
    at org.jboss.serial.io.JBossObjectInputStream.readObjectOverride(JBossObjectInputStream.java:163)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:364)
    at SerializationTest.getObjectFromByteArray(SerializationTest.java:44)
    at SerializationTest.main(SerializationTest.java:15)
Caused by: java.lang.InstantiationException: MyObj
    at java.lang.Class.newInstance(Class.java:359)
    at org.jboss.serial.classmetamodel.ClassMetaData.newInstance(ClassMetaData.java:334)
    ... 8 more

有谁知道这里出了什么问题以及如何解决这个问题?

或者如果JBossSerialization不是正确的工具,那是什么?

编辑:

正如@Dima指出的那样,SerializationException是由缺少MyObj类的公共默认构造函数引起的。但是,向MyObj添加默认构造函数不是一个选项,因为我希望能够序列化任意对象,包括那些没有默认构造函数的对象。

1 个答案:

答案 0 :(得分:2)

嗯,实际上不可能以某种方式做你想做的事,既安全又通用。

您可以查看Kryo,也可以在评论中提出建议。它确实有一种方法可以在不调用构造函数的情况下实例化对象,但默认情况下它是关闭的,有一个很好的理由

以此为例:

public class CanonicalObject {
    public static HashMap<String,CannicalObject> canons = new HahMap<~>();
    public String name;
    private CanonicalObject(String name) { 
        this.name = name;            
        canons.put(name, this);
    }
    public static synchronized CanonicalObject getCanonicalInstance(String name) {            
        CanonicalObject co = canon.get(name);
        return co == null ? new CanonicalObject(name) : co;
    }
}

(这是一个“半现实生活”的例子,因为这种模式有真正的用途。我知道“内存泄漏”,有些方法可以在实际应用中避免它,但它们无关紧要对于这个例子,所以我只是为了简单而忽略了这个问题。)

如果序列化此对象的实例,当您在另一端反序列化时,将跳过整个“规范化”部分,这可能会导致应用程序中的细微问题,真的难诊断,例如像if(canon1 != canon2) fireMissile()这样的比较导致“友军之火”,可能还有一个WorldWar III。

请注意,这里的问题比反序列化未调用的构造函数更广泛:canon.put调用很可能被放入getCanonicalInstance()而不是构造函数,这甚至会出现问题如果构造函数被调用。

这说明了为什么,作为一项政策问题,您不应该序列化不是为了序列化的对象。 IT有时可以工作,但是,如果没有,它会导致很难检测到的情况,通常更难修复。