我在我的java项目中使用aerospike作为缓存。 我更改了一个缓存对象的包。 我在一台机器上部署了新代码。获取序列化异常,因为缓存中有旧的缓存对象。
我希望旧代码在某些机器上运行,而在某些机器上运行新代码,并且两者都应该能够在aerospike缓存中获取/放置对象。
有没有办法实现这个目标? 为什么我得到这个例外。
答案 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();