查找违反非Serializable成员变量的路径

在Java中,Serialization使得对象的读取和写入非常容易。 例如,以下代码片段主要是将对象写入流所需的全部内容:

ObjectOutputStream oos = ... //Initialize your output stream Object toWrite = ... //Initialize what you want to write here oos.writeObject(toWrite); //Writes the object to the stream oos.flush(); 

只要toWrite的类实现了Serializable接口,并且所有toWrite的非transient成员变量也都是Serializable ,这样就可以正常工作。 换句话说,您尝试通过toWrite引用发送的整个Object层次结构必须是Serializable 。 假设这段代码唯一的问题是toWrite里面的东西不是Serializable

如果层次结构不完全可Serializable ,则对oos.writeObject(toWrite)的调用将抛出java.io.NotSerializableException 。 这一切都很好,除了Exception不能为您提供快速解决问题所需的一切。 它会告诉你哪些类无法序列化。 您的堆栈跟踪可能如下所示:

 java.io.NotSerializableException: some.package.and.ClassIDidntExplicitlyReference at java.io.ObjectOutputStream.writeObject0(Unknown Source) ... at my.package.MyClass.codeThatWroteTheOffendingObject(MyClass.java:###) 

这种方式指向了正确的方向,但在涉及toWrite引用的深层对象层次结构的情况下,并不总是清楚违规类引用的来源。 假设我将toWrite分配给MyClass一个实例,我的MyClass实例有一个名为nonSerializableReference的成员Object引用,该引用已设置为ClassIDidntExplicitlyReference的实例,该实例不是Serializable 。 我希望看到以下内容打印出来:

 my.package.MyClass.nonSerializableReference instanceof some.package.and.ClassIDidntExplicitlyReference 

我知道这个问题可能没有快速解决方案,并可能涉及使用Reflections。 有没有人在这里做过这件事,如果有的话,你介意分享你的见解吗?

回答

我最终使用Reflection的FieldModifier类来查找路径。 不幸的是,我不能分享我的代码(违反公司政策),但我可以告诉你我的输出。 A类包含B的实例,B包含C的实例,C包含D的实例。除D外,所有都是Serializable 。我最终使用Depth-First-Search查找路径:

 Ab -> Bc -> Cd instanceof class D 

如果我使Cd瞬态,搜索不会产生任何结果。 太酷了!

这是一个奇怪的问题 – 您希望在运行时确定在编译时应该做什么。 从理论上讲,对象的路径无关紧要 – 您需要找到引用不可序列化的东西来修复代码的对象。

也就是说,您可以使用reflection编写一个简单的递归树爬虫。 也就是说,您可以通过实现自己的ObjectInputStream来实现,该ObjectInputStream可以正确记录对象。 我建议您查看源代码以获取更多详细信息

将标志-Dsun.io.serialization.extendedDebugInfo=true传递给JVM,它会在抛出NotSerializableException时为您提供所需的信息。

我有同样的问题,我也实现了你谈到的爬虫。 如果有人仍对它感兴趣,我在这里展示了代码: 在Java中查找不可序列化字段的好方法