在各种Log4j类上改变ClassNotFoundException

时间:2015-08-13 14:33:52

标签: java classnotfoundexception

我们在基于Swing / Spring的大型应用程序中看到以下异常。在用户交互期间发生错误,触发Spring通过commons-logging warn(String, Throwable)报告警告,而后者又调用log4j。不幸的是,我无法将此问题分成一个自包含的示例。

Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: org/apache/log4j/spi/ThrowableInformation
    at org.apache.log4j.spi.LoggingEvent.<init>(LoggingEvent.java:165)
    at org.apache.log4j.Category.forcedLog(Category.java:391)
    at org.apache.log4j.Category.log(Category.java:856)
    at org.apache.commons.logging.impl.Log4JLogger.warn(Log4JLogger.java:234)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:487)
    <company specific code>
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:749)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:702)
    at java.awt.EventQueue$3.run(EventQueue.java:696)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:719)
    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)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.spi.ThrowableInformation
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 27 more

环境

  • Windows XP SP3上的Java 8u40,log4j-1.2.17,commons-logging-1.1.1,spring-4.1.2
  • 没有像OSGI,Web容器等正在使用的类加载基础设施
  • 没有使用Java代理,分析器和测试工具
  • 没有异常的-XX或-D参数

观察/我尝试的事情

  • 通过System.getProperty("java.class.path")手动打印出类路径以验证log4j JAR是否存在 - 是的,只有一个版本存在1.2.17
  • 无法加载的类在运行之间有所不同,有时是ThowableInformation,有时是ThrowableRenderer等,但总是会失败。
  • 如果将以下内容放在应用程序static void main()中,则会正确加载log4j类,并在控制台中看到警告,以后不会发生异常。 &lt; ==这确认了应用程序中可以看到log4j JAR,假设没有多个类加载器在运行...

代码

Log logger = LogFactory.getLog(getClass());
logger.warn("foo", new Exception());
  • 成功引用了以后无法从应用程序的入口点加载的类,例如ThowableInformation

代码

380        if (result == null) {
381            throw new ClassNotFoundException(name); // <=== here 
382        }
383        return result;
  • 在IOException中放置断点,以防过多的打开文件&#34;错误 - 未触发。
  • 使用-verbose和-Dsun.misc.URLClassPath.debug = true用于调试类加载的JVM选项...这表明从log4j jar加载了各种类...

例如

[Loaded org.apache.log4j.Category from file:/<path redacted>/log5j-1.2.17.jar]
[Loaded org.apache.log4j.Logger from file:/<path redacted>/log5j-1.2.17.jar]
[Loaded org.apache.log4j.Priority from file:/<path redacted>/log5j-1.2.17.jar]
  • 在ClassNotFoundException中放置断点以查找更多信息
    • 我可以看到Launcher $ AppClassLoader有一个URLClassLoader ucp
    • ucp包含URLClassPath $ FileLoader和URLClassPath $ JarLoader
    • 类型的加载器
    • 我找到了$ JarLoader的实例,它引用了log4j-1.2.17。
    • 即使sun.misc.Launcher $ AppClassLoader的同一个实例以前从JAR加载了log4j类,它似乎也不再能够......
    • 我在早期的类加载工作和后来的类加载失败之间可以看到的唯一区别是URLClassPath $ JarLoader的关闭属性在失败时设置为true ...

我的理论

  • 正在达到一些资源限制,无法再加载哪些类,但由于某些原因,未报告此类。
  • Spring,核心Java或公共日志记录正在使用无法看到log4j JAR的私有类加载基础结构。

我的问题

  • 其他人反对这个吗?
  • 可能是间歇性的ClassNotFoundException的可能原因?我该如何捕获它们?
  • 更多调试建议 - 我完全没有想法。

1 个答案:

答案 0 :(得分:0)

根本原因

URLClassLoader已关闭,这会产生Java报告ClassNotFoundException的不幸影响。

背景

为了支持使用OSGI / Spring-DM的应用程序的早期版本,我们在spring XML中使用bean,允许从正确的bundle中加载资源,这是一个可怕的hack,但它唯一的方法是在给定代码,资源和XML文件的时候完成了不同的捆绑...

<bean id="classLoader" class="org.example.internal.PluginModuleClassLoader" factory-method="getClassLoader" />
<bean id="imageLoader" class="org.example.ImageLoader">
   <property name="classLoader" ref="classLoader" />
</bean>

public class PluginModuleClassLoader {
   public static ClassLoader getClassLoader() {
      return PluginModuleClassLoader.class.getClassLoader();
   }
}

我们在6个月之前将Spring从(2.5.5升级到4.1.2)升级并删除了OSGI,因为它没有任何好处,但是由于从60多个捆绑包中删除它的成本,现在保留了现有的PluginModuleClassLoader机制。 / p>

如何关闭

有问题的用户交互会关闭一个应用程序上下文并加载另一个...这会产生破坏应用程序上下文的副作用。 Spring会自动销毁所有bean。对于classLoader bean,它调用自动查找close()方法,即URLClassLoader.close(),这意味着所有后续的查找都会因ClassNotFoundException而失败