java - readResolve 不起作用? : an instance of Guava's SerializedForm appears

标签 java deserialization guava classcastexception

在反序列化我们的一个数据结构(使用默认机制(无自定义 writeObject/readObject))期间,会出现一个 ImmutableMap$SerializedForm 实例(来自谷歌的 Guava 库)。

这样的实例不应该从 guava 的客户端可见,因为 SerializedForm 的实例被替换为 readResolve(例如,参见类 com.google.common.collect.ImmutableMap 中的“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

这是正确的,因为 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)

最佳答案

本周,我们再次遇到这个错误;但我找到了根本原因。 ObjectInputStream 使用的类加载器是高度上下文相关的(有些人会说 不确定)。这是 Sun 文档的相关部分(摘自 ObjectInputStream#resolveClass(ObjectStreamClass)):

[The class loader] is determined as follows: if there is a method on the current thread's stack whose declaring class was defined by a user-defined class loader (and was not a generated to implement reflective invocations), then it is the class loader corresponding to the closest such method to the currently executing frame; otherwise, it is null. If this call results in a ClassNotFoundException and the name of the passed ObjectStreamClass instance is the Java language keyword for a primitive type or void, then the Class object representing that primitive type or void will be returned (e.g., an ObjectStreamClass with the name "int" will be resolved to Integer.TYPE). Otherwise, the ClassNotFoundException will be thrown to the caller of this method.

在我们的应用程序中,我们有一个 Eclipse 插件 B,该插件 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);
                }
            };

关于java - readResolve 不起作用? : an instance of Guava's SerializedForm appears,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9110677/

相关文章:

java - 使用循环将元素添加到 ArrayList 并在控制台上输出它们

java - 接受来自用户的不同字符串,并使用 java 使用冒号作为分隔符显示它们

java - 域对象的更改通知(Hibernate/Java)

Java - 在类上强制实例化

java - Guava Set difference 是如何工作的?

java - 在 Spring MVC 应用程序中使用 jclouds 创建 BlobContext 时出错

java - 创建或替换并解析 Java 源代码,我无法使用 Java 源代码创建函数

java - 当@JacksonXmlProperty.localName 匹配@JacksonXmlRootElement.localName 时无法反序列化展开的列表

json - Jackson:是否可以将父对象的属性包含到嵌套对象中?

java - Guava 布隆过滤器插入后的预期误报率很高