如果我改变了java类的包。来自旧序列化版本的反序列化是否仍然有效?

时间:2016-05-19 13:09:26

标签: java caching serialization aerospike

我在我的java项目中使用aerospike作为缓存。 我更改了一个缓存对象的包。 我在一台机器上部署了新代码。获取序列化异常,因为缓存中有旧的缓存对象。

我希望旧代码在某些机器上运行,而在某些机器上运行新代码,并且两者都应该能够在aerospike缓存中获取/放置对象。

有没有办法实现这个目标? 为什么我得到这个例外。

3 个答案:

答案 0 :(得分:0)

类所属的包是该类标识的基本部分,正如包名是该类的完全限定名称的一部分所反映的那样。对于所有实际意图和目的,更改分配了类的包将删除原始类,并将其替换为完全不同的类。

特别是,如果序列化名为" my.package.MyClass"的类的实例。通过Java序列化,然后成功反序列化结果总是产生一个名为" my.package.MyClass"的类的实例。如果不能加载这样的类,或者加载的类的序列化版本与序列化版本的序列化版本不匹配,则反序列化将失败。

如果您将旧类与新类一起保留,那么您可以修补新版本应用程序中的反序列化问题。只需为旧类的对象做好准备,并在反序列化后立即将它们转换为新类的实例。但是,如果您希望新版本的应用程序与旧版本一起使用,那么当新版本序列化受影响类的对象时,您还必须执行相反的操作,否则您只会导致旧应用程序出现反序列化错误。此时,您应该考虑是否更改包名称是非常重要的。

总的来说,Java序列化不太适合对象存储。它主要针对对象通信。您可以考虑切换到基于XML,JSON或YAML的序列化格式,至少可以在缓存和消费者之间进行切换。这样的更改当然与您的应用程序的旧版本不兼容,但显然,您已经执行了更改包。

答案 1 :(得分:0)

通常,序列化算法执行以下操作:

  • 它写出与实例关联的类的元数据。
  • 它递归地写出超类的描述,直到找到java.lang.object
  • 一旦完成编写元数据信息,它就会以与实例关联的实际数据开始。但这一次,它从最顶层的超类开始。
  • 它递归地写入与实例关联的数据,从最小的类开始到最派生的类。

在反序列化期间,它将尝试重新实例化类,但由于包名已被更改,因此无法找到元数据中指定的类并失败。

查看以下工具可能是值得的:jDeserialize。它不会实例化流中描述的任何类;相反,它构建了类型,实例和值的中间表示。因此,它可以分析流而无需访问生成它们的类代码。

答案 2 :(得分:0)

您可以将 ObjectInputStream 子类化并使用它来拦截 readClassDescriptor,如果被反序列化的类的包与您的不同,您可以更改它。但是,如果被反序列化的类然后包含需要反序列化的其他类,则情况会变得更加复杂。

public class MyObjectInputStream extends ObjectInputStream {

    public MyObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    @Override
    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();
        String className = resultClassDescriptor.getName();
        if (className.equals("my.old.pacakgename.MyClass")) {
            return ObjectStreamClass.lookup(MyClass.class);
        }
        return resultClassDescriptor;
    }
}

使用这个

ObjectInputStream ois = new MyObjectInputStream(stream);
MyClass myObject = (MyClass) ois.readObject();