readResolve不工作?:出现了Guava的SerializedForm实例

Clé*_*lin 11 java classcastexception guava deserialization

在我们的一个数据结构的反序列化过程中(使用默认机制(没有自定义writeObject/readObject)),会出现一个ImmutableMap $ SerializedForm实例(来自google的Guava库).

这样的实例不应该从guava的客户端看到,因为SerializedForm的实例被替换为readResolve(参见com.google.common.collect.Immutable类中的"writeReplace").

因此反序列化失败,并显示以下消息:

java.lang.ClassCastException: cannot assign instance of com.google.common.collect.ImmutableMap$SerializedForm
to field .. of type java.util.Map in instance of com.blah.C
Run Code Online (Sandbox Code Playgroud)

这是正确的,因为ImmutableMap $ SerializedForm不是java.util.Map的子类型,但它应该被替换.出了什么问题?

我们在com.blah.C类中没有自定义的writeObject/readObject.我们在父对象(包含com.blah.C)中有自定义序列化代码.

更新,这是堆栈跟踪的顶部:

java.lang.ClassCastException: cannot assign instance of com.google.common.collect.ImmutableSet$SerializedForm to field com.blah.ast.Automaton.bodyNodes of type java.util.Set in instance of com.blah.ast.Automaton
at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2039)
at java.io.ObjectStreamClass.setObjFieldValues(ObjectStreamClass.java:1212)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1952)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1870)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
at java.util.ArrayList.readObject(ArrayList.java:593)
at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1946)
at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:479)
at com.blah.ast.AstNode.readObject(AstNode.java:189)
at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
at java.util.ArrayList.readObject(ArrayList.java:593)
at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1848)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1946)
at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:479)
at com.blah.ast.AstNode.readObject(AstNode.java:189)
Run Code Online (Sandbox Code Playgroud)

Clé*_*lin 7

本周,我们又面临这个错误; 但我找到了根本原因.ObjectInputStream使用的类加载器是高度依赖于上下文的(有些人会说是不确定的).以下是Sun文档的相关部分(它是ObjectInputStream#resolveClass(ObjectStreamClass)的摘录):

[类加载器]确定如下:如果当前线程的堆栈上有一个方法,其声明类是由用户定义的类加载器定义的(并且不是为实现反射调用而生成的),那么它就是类加载器对应于与当前执行帧最接近的这种方法; 否则,它为空.如果此调用导致ClassNotFoundException并且传递的ObjectStreamClass实例的名称是基本类型或void的Java语言关键字,则将返回表示该基本类型或void的Class对象(例如,名为"int"的ObjectStreamClass) "将被解析为Integer.TYPE).否则,将向此方法的调用者抛出ClassNotFoundException.

在我们的应用程序中,我们有一个Eclipse插件B依赖于一个实用程序专用插件A.我们反序列化其类在B中的对象,但反序列化是在A中启动的(在那里创建一个ObjectInputStream),这就是问题所在.很少(即取决于文档所说的调用堆栈)反序列化选择了错误的类加载器(一个无法加载B类的加载器).为了解决这个问题,我们将一个适当的加载器从顶级反序列化调用程序(在B中)传递给A中的实用程序方法.此方法现在使用自定义ObjectInputStream,如下所示(注意自由变量"loader"):

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)) {
                @SuppressWarnings("rawtypes")
                @Override
                protected Class resolveClass(ObjectStreamClass objectStreamClass)
                        throws IOException, ClassNotFoundException {
                    return Class.forName(objectStreamClass.getName(), true, loader);
                }
            };
Run Code Online (Sandbox Code Playgroud)