如何安全地解决此Java上下文类加载器问题?
我的数百名用户中只有一个在启动Java桌面应用程序时遇到问题。 只有三分之一的时间才开始。 另外三分之二的时间在启动时抛出NullPointerException:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at java.util.Hashtable.put(Hashtable.java:394) at javax.swing.JEditorPane.registerEditorKitForContentType(JEditorPane.java:1327) at javax.swing.JEditorPane.registerEditorKitForContentType(JEditorPane.java:1309) at javax.swing.JEditorPane.loadDefaultKitsIfNecessary(JEditorPane.java:1387) at javax.swing.JEditorPane.getKitTypeRegistry(JEditorPane.java:1344) at javax.swing.JEditorPane.getEditorKitClassNameForContentType(JEditorPane.java:1340) at javax.swing.JTextPane.(JTextPane.java:76) at myapp.Launcher$1.run(Launcher.java:13) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209) at java.awt.EventQueue.dispatchEvent(EventQueue.java:633) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188) at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
我已经按照堆栈跟踪找到原因
Thread.currentThread().getContextClassLoader()
在JEditorPane中返回null。
谷歌搜索显示,这是一个零星的,非常罕见的,神秘的问题,影响了一些人。
我的问题是,作为一种解决办法,我能做些什么? 如果我在创建EditorPane之前调用它,这可能会有效:
Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader());
但我并不像我想的那样真正理解类加载器(我试图更好地理解它们)。 我觉得改变EDT中的contextClassLoader可能会产生不良影响。
任何想法我能做什么?
编辑:我与熟悉Java ClassLoaders的人有一些通信。 这似乎是一个模糊的ClassLoader竞争条件。 也就是Java中的一个bug。
Thread.currentThread().getContextClassLoader()
如果JEditorPane.registerEditorKitForContentType
中的代码未在上面的代码中检查null返回值,则这是JEditorPane
的错误。 请注意, MyClass.class.getClassLoader()
也可能返回null 。 您唯一可以依赖的是系统ClassLoader 。
为调用设置上下文ClassLoader
的模式通常如下所示:
Thread thread = Thread.currentThread(); ClassLoader old = thread.getContextClassLoader(); thread.setContextClassLoader(fooClassLoader); try { // do call that depends on context ClassLoader } finally { thread.setContextClassLoader(old); }
应该通过setContextClassLoader
设置的值将取决于使用它的代码的意图以及您正在运行的ClassLoader
框架的设计。
在独立的应用程序中,您可能只需使用此ClassLoader
(将ref传递给当前类)即可:
private ClassLoader findClassLoaderForContext(Class> c) { ClassLoader context = Thread.currentThread().getContextClassLoader(); ClassLoader me = c.getClassLoader(); ClassLoader system = ClassLoader.getSystemClassLoader(); return (context == null) ? (me == null) ? system : me : context; }
在ClassLoader敏感的插件框架(Java EE服务器将是一个主要的例子)中,理解加载方案的性质和用法是值得的。