管理多个版本的序列化Java对象 [英] Managing several versions of serialized Java objects

查看:78
本文介绍了管理多个版本的序列化Java对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个程序,由于某种原因需要处理旧版本的序列化对象。

Lets say that I have a program that for some reason need to handle old versions of serialized objects.

例如:反序列化时,可能遇到其中一个版本。

Eg: when deserializing, one of these versions may be encountered.

class Pet {
    private static final long serialVersionUID = 1L;
    int paws;
}

class Pet {
    private static final long serialVersionUID = 2L;
    long paws; // handle marsian centipedes
    boolean sharpTeeth;
}

让我们假设(逻辑上)可以将旧对象转换为新对象对象使用一些聪明的策略来设置不存在的字段等,但是:

Lets assume that it's (logically) possible to convert an old object to a new object using some clever strategy to set nonexistant fields etc etc, but:

如何安排我的源代码?在编写转换器时,我可能需要在同一源代码树中使用这两个版本,但是如何在eclipse中处理它。

How do I arrange my source code? I would probably need both versions in the same source tree when writing a converter, but how do I handle that in , say, eclipse.

我应该在一个类中进行反序列化加载器,如果失败尝试使用另一个使用旧版本的类加载器(等等),还是有更好的方法?

Should I do deserialization in one class loader, and if that fails try using another class loader that uses an older version (and so on), or are there better ways?

什么是最好的策略?

推荐答案


让我们假设(逻辑上)可以将旧对象转换为新对象一些聪明的策略来设置不存在的字段等等...我如何安排我的源代码?

Lets assume that it's (logically) possible to convert an old object to a new object using some clever strategy to set nonexistant fields etc etc... How do I arrange my source code?

我看到两种方法来处理这个问题。首先,除非要抛出 InvalidClassException ,否则不应更改 serialVersionUID 。第二条规则是更改字段的类型,而只是添加或删除序列化自动处理的字段。例如,如果序列化文件的类版本具有 boolean sharpTeeth; 但该类没有该字段,则在反序列化期间将忽略该字段。如果反序列化的类具有 sharpTeeth 字段,但文件没有,那么它将被初始化为其默认值, false 在这种情况下。

I see two ways of handling this. First off, you should never change the serialVersionUID unless you want InvalidClassException to be thrown. The second rule is to not change the types of fields but to only add or remove fields which serialization handles automagically. For example, if a serialized file has the version of the class which has boolean sharpTeeth; but the class doesn't have that field then it will be ignored during deserialization. If the deserialized class has the sharpTeeth field but the file doesn't then it will get initialized to its default value, false in this case.

对于要尝试同时处理向前和向后兼容性的分布式系统,这一点尤其重要。您不希望升级应用程序A的版本并破坏依赖于A的另一个应用程序B.通过不更改 serialVersionUID 但只添加或删除字段,您可以执行该操作。实体的更高版本需要支持旧版本而没有较新字段中的值,但较旧的实体不会介意新字段是否可用。这也意味着您也不应该更改字段的比例。

This is especially important with distributed systems where you want to try to handle both forwards and backwards compatibility. You don't want to upgrade a version of application A and break another application B which depends on A. By not changing the serialVersionUID but just adding or removing fields you can do that. Later versions of your entity need to support older versions without values in newer fields but older entities won't mind if new fields are available. This also means that you shouldn't change a field's scale as well.

序列化非常聪明,但它不处理对字段的类型更改。您不应该只将爪子 int 更改为 long 。相反,我建议添加 long pawsLong 或其他一些代码来编写代码以处理 int paws long pawsLong 有一个值。

Serialization is pretty smart but it does not handle type changes to fields. You shouldn't just change paws from an int to a long. Instead, I'd recommend adding a long pawsLong or some such and writing your code to handle the possibility of there being int paws or long pawsLong having a value.

public long getPaws() {
    if (pawsLong > 0) {
        return pawsLong;
    } else {
        // paws used to be an integer
        return paws;
    }
}

你也可以写自己的 readObject 在反序列化时进行转换的方法:

You could also write your own readObject method to do the conversion at de-serialization time:

private void readObject(java.io.ObjectInputStream in) {
    super.readObject(in);
    // paws used to be an integer
    if (pawsLong == 0 && paws != 0) {
        pawsLong = paws;
    }
}

如果这不适合你,那么自定义序列化是要走的路。你必须从头开始这样做,然后定义自定义 readObject(...) writeObject(...)具有内部版本ID的方法。类似于:

If this doesn't work for you then custom serialization is the way to go. You have to start from the beginning doing this however and define custom readObject(...) and writeObject(...) methods with an internal version id. Something like:

// never change this
private static final long serialVersionUID = 3375159358757648792L;
// only goes up
private static final int INTERNAL_VERSION_ID = 2;
...
// NOTE: in version #1, this was an int
private long paws;

private void readObject(java.io.ObjectInputStream in) {
    int version = in.readInt();
    switch (version) {
        case 1 :
            paws = in.readInt();
            ...
        case 2 :
            paws = in.readLong();
            ...

private void writeObject(java.io.ObjectOutputStream out) {
    out.writeInt(INTERNAL_VERSION_ID);
    out.writeLong(paws);
    ...

但是此方法无法帮助您实现向前兼容性。版本1阅读器将无法理解版本2序列化输入。

But this method does not help you with forwards compatibility. A version 1 reader won't understand version 2 serialization input.


我应该在一个类加载器中进行反序列化,如果失败,请尝试使用另一个使用旧版本的类加载器(依此类推),还是有更好的方法?

Should I do deserialization in one class loader, and if that fails try using another class loader that uses an older version (and so on), or are there better ways?

我不会建议任何这些方法。听起来很难维护。

I would not suggest any of these methods. Sounds very difficult to maintain.

这篇关于管理多个版本的序列化Java对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆