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

时间:2014-08-01 07:04:10

标签: java serialization find field

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

java.io.NotSerializableException: sun.java2d.SunGraphics2D

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

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

提前谢谢

的Lukas

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

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)

2 个答案:

答案 0 :(得分:9)

好吧,受Ben Lawry的想法的启发,然而,Ben Lawry没有提供实际的实现,我还编写了一个类爬虫,它可以找到"不可序列化的字段",即非静态的,非瞬态字段,它们本身没有实现Serializable接口,或者包含非静态,非瞬态且不实现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<Class<?>> alreadyCrawled, HashMap<Field, HashSet<String>> 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<Field, HashSet<String>> initiateCrawling(Collection<Class<?>> roots) {
        HashMap<Field, HashSet<String>> 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<Class<?>> roots = new LinkedList<>();
        roots.add(JComponent.class); // ADD YOUR CLASSES HERE.
        HashMap<Field, HashSet<String>> 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("<UnSer> "
                    + 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]:
<UnSer> Border border (javax.swing.JComponent) => []
<UnSer> ComponentInputMap windowInputMap (javax.swing.JComponent) => [JComponent component]
<UnSer> VetoableChangeSupport vetoableChangeSupport (javax.swing.JComponent) => [VetoableChangeListenerMap map, Object source]
<UnSer> SingleSelectionModel selectionModel (javax.swing.JPopupMenu) => []
<UnSer> JPopupMenu popupMenu (javax.swing.JComponent) => [SingleSelectionModel selectionModel]
<UnSer> Object source (java.beans.VetoableChangeSupport) => []
<UnSer> VetoableChangeListenerMap map (java.beans.VetoableChangeSupport) => []
<UnSer> JComponent component (javax.swing.ComponentInputMap) => [VetoableChangeSupport vetoableChangeSupport, JPopupMenu popupMenu, Border border, InputVerifier inputVerifier]
<UnSer> InputVerifier inputVerifier (javax.swing.JComponent) => []

答案 1 :(得分:1)

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

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

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

根据您的应用程序,Ben Lawry针对类似问题may be easier to implement的自动解决方案,而不是手动搜索上述内容。如果该路径有效,它也可能更加彻底。你能得到一个堆栈跟踪吗?