如果我换了一个javaclass的包。旧序列化版本的反序列化仍然有效吗?
If i changed the package of a java class. Will the deserialization from the old serialized version still work?
我在 java 项目中使用 aerospike 作为缓存。
我更改了其中一个缓存对象的包。
我在一台机器上部署了新代码。获取序列化异常,因为缓存中有旧的缓存对象。
我希望旧代码在某些机器上 运行 而新代码在某些机器上并且两者都应该能够 get/put aerospike 缓存中的对象。
有办法实现吗?
为什么我会收到此异常。
class 所属的包是 class 身份的基本部分,正如包名称是 class 的完整部分的一部分所反映的那样-合格的名字。出于所有实际意图和目的,更改分配了 class 的包会删除原始 class 并将其替换为完全不同的 class.
特别是,如果您通过 Java 序列化序列化一个名为 "my.package.MyClass" 的 class 的实例,那么成功反序列化结果总是会产生一个 class 的实例命名为 "my.package.MyClass"。如果没有这样的class可以加载,或者如果加载的序列化版本与序列化的序列化版本不匹配,则反序列化将失败。
如果您保留旧的 class 和新的 class,那么您也许可以在新版本的应用程序中修补反序列化问题。只需为旧 class 的对象做好准备,并在反序列化后立即将它们转换为新 class 的实例。但是如果你想让新版本的应用程序与旧版本的应用程序很好地兼容,那么你也必须在新版本应用程序序列化受影响的class对象时做相反的事情,否则你只会导致旧应用程序反序列化错误。在这一点上,您应该考虑更改包名是否真的那么重要。
总体而言,Java 序列化不太适合对象存储。它主要针对对象通信。您可能会考虑切换到 XML-、JSON- 或基于 YAML 的序列化格式,至少您可以在缓存和消费者之间游刃有余。这样的更改当然会与您的应用程序的旧版本不兼容,但显然,您已经执行的包更改也是如此。
一般来说,序列化算法会执行以下操作:
- 它写出与实例关联的 class 的元数据。
- 递归写出superclass的描述,直到找到
java.lang.object
.
- 一旦它完成元数据信息的写入,它就会从与实例关联的实际数据开始。但是这次,是从最顶层的superclass.
开始
- 它递归地写入与实例关联的数据,从最小的超级class开始到最派生的class。
在反序列化期间,它会尝试重新实例化 class,但由于包名称已更改,它无法找到元数据中指定的 class,因此失败。
可能值得看看以下工具:jDeserialize。它不会实例化流中描述的任何 classes;相反,它构建了类型、实例和值的中间表示。因此,它可以在不访问生成流的 class 代码的情况下分析流。
您可以子class ObjectInputStream 并使用它来拦截readClassDescriptor,如果被反序列化的class 包与您的包不同,您可以更改它。但是,如果被反序列化的 class 然后包含其他需要反序列化的 classes,它会变得更加复杂。
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 项目中使用 aerospike 作为缓存。 我更改了其中一个缓存对象的包。 我在一台机器上部署了新代码。获取序列化异常,因为缓存中有旧的缓存对象。
我希望旧代码在某些机器上 运行 而新代码在某些机器上并且两者都应该能够 get/put aerospike 缓存中的对象。
有办法实现吗? 为什么我会收到此异常。
class 所属的包是 class 身份的基本部分,正如包名称是 class 的完整部分的一部分所反映的那样-合格的名字。出于所有实际意图和目的,更改分配了 class 的包会删除原始 class 并将其替换为完全不同的 class.
特别是,如果您通过 Java 序列化序列化一个名为 "my.package.MyClass" 的 class 的实例,那么成功反序列化结果总是会产生一个 class 的实例命名为 "my.package.MyClass"。如果没有这样的class可以加载,或者如果加载的序列化版本与序列化的序列化版本不匹配,则反序列化将失败。
如果您保留旧的 class 和新的 class,那么您也许可以在新版本的应用程序中修补反序列化问题。只需为旧 class 的对象做好准备,并在反序列化后立即将它们转换为新 class 的实例。但是如果你想让新版本的应用程序与旧版本的应用程序很好地兼容,那么你也必须在新版本应用程序序列化受影响的class对象时做相反的事情,否则你只会导致旧应用程序反序列化错误。在这一点上,您应该考虑更改包名是否真的那么重要。
总体而言,Java 序列化不太适合对象存储。它主要针对对象通信。您可能会考虑切换到 XML-、JSON- 或基于 YAML 的序列化格式,至少您可以在缓存和消费者之间游刃有余。这样的更改当然会与您的应用程序的旧版本不兼容,但显然,您已经执行的包更改也是如此。
一般来说,序列化算法会执行以下操作:
- 它写出与实例关联的 class 的元数据。
- 递归写出superclass的描述,直到找到
java.lang.object
. - 一旦它完成元数据信息的写入,它就会从与实例关联的实际数据开始。但是这次,是从最顶层的superclass. 开始
- 它递归地写入与实例关联的数据,从最小的超级class开始到最派生的class。
在反序列化期间,它会尝试重新实例化 class,但由于包名称已更改,它无法找到元数据中指定的 class,因此失败。
可能值得看看以下工具:jDeserialize。它不会实例化流中描述的任何 classes;相反,它构建了类型、实例和值的中间表示。因此,它可以在不访问生成流的 class 代码的情况下分析流。
您可以子class ObjectInputStream 并使用它来拦截readClassDescriptor,如果被反序列化的class 包与您的包不同,您可以更改它。但是,如果被反序列化的 class 然后包含其他需要反序列化的 classes,它会变得更加复杂。
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();