如果我更改了java类的包.旧序列化版本的反序列化是否仍然有效



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

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

有办法做到这一点吗?我为什么会得到这个例外。

您可以对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();

类所属的包是该类标识的基本组成部分,包名是该类完全限定名称的一部分。对于所有实际的意图和目的,更改为其分配类的包会删除原始类,并用一个完全不同的类替换它。

特别是,如果您通过Java序列化来序列化名为"my.package.MyClass"的类的实例,那么成功地反序列化结果总是会生成名为"my.package.MyClass"的类实例。如果无法加载此类类,或者加载的类的序列化版本与序列化的类不匹配,则反序列化将失败。

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

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

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

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

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

也许值得一看以下工具:jDeserialize。它不实例化流中描述的任何类;相反,它构建了类型、实例和值的中间表示。因此,它可以在不访问生成流的类代码的情况下分析流。

相关内容

最新更新