readResolve无法正常工作? :出现了一个Guava的SerializedForm实例 [英] readResolve not working ? : an instance of Guava's SerializedForm appears

查看:126
本文介绍了readResolve无法正常工作? :出现了一个Guava的SerializedForm实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

During deserialization of one of our data structure (using the default mechanism (no custom writeObject/readObject)), an instance of ImmutableMap$SerializedForm (from google's Guava library) shows up.

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

Such an instance should not be visible from clients of guava because instances of SerializedForm are replaced using readResolve (see for example "writeReplace" in class com.google.common.collect.ImmutableMap).

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

Hence deserialization fails with the following message :

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的子类型,而是
它应该被替换。出了什么问题?

This is right since ImmutableMap$SerializedForm is not a subtype of java.util.Map, yet it should have been replaced. What is going wrong ?

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

We have no custom writeObject/readObject in class com.blah.C. We do have custom serialization code in parent objects (that contain 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使用的类加载器是高度依赖于上下文的(有些人会说 indeterministic )。以下是Sun文档的相关部分(它是ObjectInputStream#resolveClass(ObjectStreamClass)的摘录):

This week, we faced again this bug; but I found the root reason. The class loader used by ObjectInputStream is highly-context dependant (some would say indeterministic). Here is the relevant part of Sun's documentation (it's an excerpt from ObjectInputStream#resolveClass(ObjectStreamClass)):


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

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

In our application, we have an Eclipse plugin B that depends on an utility-only plugin A. We were deserializing objects whose classes are in B, but deserialization was initiated in A (creating an ObjectInputStream there), and that was the problem. Rarely (i.e. depending on the call stack as the doc says) deserialization chose the wrong class loader (one that could not load B-classses). To solve this problem, we passed an appropriate loader from the top-level deserialization caller (in B) to the utility method in A. This method now uses a custom ObjectInputStream as follows (note the free variable "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);
                }
            };

这篇关于readResolve无法正常工作? :出现了一个Guava的SerializedForm实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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