在各种 Log4j 上改变 ClassNotFoundException 类

Varying ClassNotFoundException on various Log4j classes

我们在基于大型 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.access0(EventQueue.java:97)
    at java.awt.EventQueue.run(EventQueue.java:702)
    at java.awt.EventQueue.run(EventQueue.java:696)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain.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

环境

观察/我尝试过的事情

代码

Log logger = LogFactory.getLog(getClass());
logger.warn("foo", new Exception());

代码

380        if (result == null) {
381            throw new ClassNotFoundException(name); // <=== here 
382        }
383        return result;

例如

[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]

我的理论

我的问题

根本原因

URLClassLoader 已关闭,这造成了 Java 报告 ClassNotFoundException 的不幸影响。

背景

为了支持使用 OSGI/Spring-DM 的应用程序的早期版本,我们在 spring XML 中有 bean,它允许从正确的包中加载资源,这太可怕了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();
   }
}

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

它是如何关闭的

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