在Java中查找不可序列化字段的好方法

我有一个非常复杂的Java对象来序列化(几周前工作正常)。 在此期间实现了很多之后,序列化现在无法抛出以下exception:

java.io.NotSerializableException: sun.java2d.SunGraphics2D 

我已经检查了所有似乎相关的类,但是在其中任何一个中都没有找到一个包含Graphics2D类型的字段(或类似的类型,如BufferedImage等)。 正如我所看到的,问题也可能是我正在使用一个类(来自库等),它本身是可序列化的,但有一个不可序列化类型的Graphics2D字段。

所以,我的问题是:是否有一种“好”的方法来找到序列化失败的地方? 检查所有类和所做的所有更改似乎都不可行。

先感谢您

卢卡斯

PS。 以下是exception的完整堆栈跟踪:

 java.io.NotSerializableException: sun.java2d.SunGraphics2D at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174) at java.io.ObjectOutputStream.access$300(ObjectOutputStream.java:162) at java.io.ObjectOutputStream$PutFieldImpl.writeFields(ObjectOutputStream.java:1707) at java.io.ObjectOutputStream.writeFields(ObjectOutputStream.java:482) at java.awt.Container.writeObject(Container.java:3697) at sun.reflect.GeneratedMethodAccessor17.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174) at java.io.ObjectOutputStream.access$300(ObjectOutputStream.java:162) at java.io.ObjectOutputStream$PutFieldImpl.writeFields(ObjectOutputStream.java:1707) at java.io.ObjectOutputStream.writeFields(ObjectOutputStream.java:482) at java.awt.Container.writeObject(Container.java:3697) at sun.reflect.GeneratedMethodAccessor17.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174) at java.io.ObjectOutputStream.access$300(ObjectOutputStream.java:162) at java.io.ObjectOutputStream$PutFieldImpl.writeFields(ObjectOutputStream.java:1707) at java.io.ObjectOutputStream.writeFields(ObjectOutputStream.java:482) at java.awt.Container.writeObject(Container.java:3697) at sun.reflect.GeneratedMethodAccessor17.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174) at java.io.ObjectOutputStream.access$300(ObjectOutputStream.java:162) at java.io.ObjectOutputStream$PutFieldImpl.writeFields(ObjectOutputStream.java:1707) at java.io.ObjectOutputStream.writeFields(ObjectOutputStream.java:482) at java.awt.Container.writeObject(Container.java:3697) at sun.reflect.GeneratedMethodAccessor17.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at java.awt.Window.writeObject(Window.java:2943) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at java.awt.AWTEventMulticaster.save(AWTEventMulticaster.java:946) at java.awt.Window.writeObject(Window.java:2931) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at java.util.LinkedList.writeObject(LinkedList.java:1131) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at eas.simulation.SerializableSimulationState.save(SerializableSimulationState.java:121) at eas.plugins.standard.visualization.ControlPanel.actionPerformed(ControlPanel.java:202) at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022) at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346) at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402) at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252) at java.awt.Component.processMouseEvent(Component.java:6527) at javax.swing.JComponent.processMouseEvent(JComponent.java:3321) at java.awt.Component.processEvent(Component.java:6292) at java.awt.Container.processEvent(Container.java:2234) at java.awt.Component.dispatchEventImpl(Component.java:4883) at java.awt.Container.dispatchEventImpl(Container.java:2292) at java.awt.Component.dispatchEvent(Component.java:4705) at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898) at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533) at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462) at java.awt.Container.dispatchEventImpl(Container.java:2278) at java.awt.Component.dispatchEvent(Component.java:4705) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:746) at java.awt.EventQueue.access$400(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:697) at java.awt.EventQueue$3.run(EventQueue.java:691) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86) at java.awt.EventQueue$4.run(EventQueue.java:719) at java.awt.EventQueue$4.run(EventQueue.java:717) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75) at java.awt.EventQueue.dispatchEvent(EventQueue.java:716) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) 

可以假定库类是可正确序列化的,即将所有非可序列化字段声明为瞬态,因此不应该是错误的来源。

如果您的代码使用ContextedRuntimeExceptions,则它们是上述规则的一个例外,因为它们引用的对象可能不是可序列化的,这在方法addContextValue()和setContextValue() 的Javadoc中有说明 。 如果上下文值是问题,它将解释为什么可疑类在没有这样的字段的情况下获得对Graphics2D对象的引用。

我在SunGraphics2D上复制NotSerializableException的成功有限,而没有在其名称中留下明显的“Graphics”字段,这可能是由于在更简单的类结构上进行测试。 我发现除了上下文exception之外,收集是一个很好的嫌疑人,但前提是他们使用原始类型。 如果它们有时仅填充图形,则集合也可能导致间歇性故障。

根据您的应用,Ben Lawry针对类似问题的自动化解决方案可能比手动搜索上述内容更容易实现 。 如果该路径有效,那么它也可能更加彻底。 你能得到一个堆栈跟踪吗?

好吧,受到Ben Lawry的想法的启发,然而,Ben Lawry没有提供实际的实现,我还编写了一个类爬虫,它找到了“不可序列化的字段”,即非静态的非瞬态字段,它们要么不实现可序列化接口本身或包含非静态,非瞬态且不实现Serializable接口的字段。

您可以在main方法的列表中添加几个要测试的类(请参阅注释)。 输出是来自这些类的字段列表或任何不能通过上述定义序列化的引用类(但是,它们不完全等于实际可序列化的字段,但捕获后者的超集); 对于本身实现Serializable接口的每个字段,给出了该字段类型中引用的字段列表,这导致字段不可序列化(如果列表为空,则字段本身不实现Serializable接口)。

这是代码,希望它也能帮助其他人:

 import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import javax.swing.JComponent; public class Crawler { public static boolean crawlRecursively(Field field, HashSet> alreadyCrawled, HashMap> badFields) { if (alreadyCrawled.contains(field.getType())) { return !badFields.keySet().contains(field); } alreadyCrawled.add(field.getType()); if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) || field.getType().isPrimitive()) { return true; } else if (Serializable.class.isAssignableFrom(field.getType())) { boolean allGood = true; for (Field f : field.getType().getDeclaredFields()) { boolean isGood = crawlRecursively(f, alreadyCrawled, badFields); if (!isGood) { if (!badFields.containsKey(field)) { badFields.put(field, new HashSet<>()); } badFields.get(field).add(f.getType().getSimpleName() + " " + f.getName()); allGood = false; } } return allGood; } else { if (!badFields.containsKey(field)) { badFields.put(field, new HashSet<>()); } return false; } } public static HashMap> initiateCrawling(Collection> roots) { HashMap> badFields = new HashMap<>(); for (Class root : roots) { for (Field f : root.getDeclaredFields()) { crawlRecursively(f, new HashSet<>(), badFields); } } return badFields; } public static void main(String[] args) { LinkedList> roots = new LinkedList<>(); roots.add(JComponent.class); // ADD YOUR CLASSES HERE. HashMap> badFields = initiateCrawling(roots); if (badFields.keySet().size() == 0) { System.out.println("All fields are serializable (not having checked the given class(es) themselves)."); } else { System.out.println("The following fields are not serializable in the class tree(s) given by " + roots + ":"); } for (Field field : badFields.keySet()) { System.out.println(" " + field.getType().getSimpleName() + " " + field.getName() + " (" + field.getDeclaringClass().getName() + ") => " + badFields.get(field)); } } } 

类JComponent的示例输出:

 The following fields are not serializable in the class tree(s) given by [class javax.swing.JComponent]:  Border border (javax.swing.JComponent) => []  ComponentInputMap windowInputMap (javax.swing.JComponent) => [JComponent component]  VetoableChangeSupport vetoableChangeSupport (javax.swing.JComponent) => [VetoableChangeListenerMap map, Object source]  SingleSelectionModel selectionModel (javax.swing.JPopupMenu) => []  JPopupMenu popupMenu (javax.swing.JComponent) => [SingleSelectionModel selectionModel]  Object source (java.beans.VetoableChangeSupport) => []  VetoableChangeListenerMap map (java.beans.VetoableChangeSupport) => []  JComponent component (javax.swing.ComponentInputMap) => [VetoableChangeSupport vetoableChangeSupport, JPopupMenu popupMenu, Border border, InputVerifier inputVerifier]  InputVerifier inputVerifier (javax.swing.JComponent) => []