log4j 和线程上下文类加载器
我是 Java 的新手,刚刚开始弄清楚类加载器的概念。现在,我在使用 log4j 时遇到一些关于线程上下文类加载器的问题。
我收到以下错误:“org.apache.log4j.ConsoleAppender”对象无法分配给“org.apache.log4j.Appender”变量。类“org.apache.log4j.Appender”由[java.net.URLClassLoader@105691e]加载,而类型“org.apache.log4j.ConsoleAppender”的对象由[sun.misc.Launcher$AppClassLoader@16930e2]加载。无法实例化名为“CONSOLE”的附加程序。
我的应用程序大致是这样工作的:在 init URLClassLoader #1 上构建并加载一些类,这些类使用 log4j。稍后,构造了 URLClassLoader #2(以 URLClassLoader #1 作为其父级)并加载更多类,这些类也使用 log4j。当使用 URLClassLoader #2 加载这些类时,会出现上述错误消息(还有更多错误消息具有相同的问题)。
我当前所做的解决方法是在加载有问题的类之前将当前线程上下文类加载器设置为 URLClassLoader #2,然后将其重置为旧的类加载器:
ClassLoader urlClassLoader; // this is URLClassLoader #2
Thread thread = Thread.currentThread();
ClassLoader loader = thread.getContextClassLoader();
thread.setContextClassLoader(urlClassLoader);
try {
urlClassLoader.loadClass(...)
} finally {
thread.setContextClassLoader(loader);
}
虽然这有效,但我不确定这是否是正确的方法。
对此事的任何见解将不胜感激。另外,为什么 log4j 强迫我搞乱线程上下文类加载器?为什么不让我传入一个类加载器(如果不这样做,则使用默认的类加载器)而不是使用线程的类加载器?
I'm a newbie to Java and just starting to figure out the concept of class loaders. Right now I am having some issues with log4j regarding its use of the thread context classloader.
I'm getting the following errors: A "org.apache.log4j.ConsoleAppender" object is not assignable to a "org.apache.log4j.Appender" variable. The class "org.apache.log4j.Appender" was loaded by [java.net.URLClassLoader@105691e] whereas object of type "org.apache.log4j.ConsoleAppender" was loaded by [sun.misc.Launcher$AppClassLoader@16930e2]. Could not instantiate appender named "CONSOLE".
My application works roughly this way: On init URLClassLoader #1 is constructed and loads some classes, these classes use log4j. Later on URLClassLoader #2 is constructed (which has URLClassLoader #1 as it's parent) and loads some more classes, these classes also use log4j. When URLClassLoader #2 is used to load these classes the above error message appears (there are a couple more with the same issue).
The current workaround I did was to set the current thread context classloader to URLClassLoader #2 before loading the problematic classes, and resetting it to the old one afterwards:
ClassLoader urlClassLoader; // this is URLClassLoader #2
Thread thread = Thread.currentThread();
ClassLoader loader = thread.getContextClassLoader();
thread.setContextClassLoader(urlClassLoader);
try {
urlClassLoader.loadClass(...)
} finally {
thread.setContextClassLoader(loader);
}
While this works, I am not sure if it's the right approach.
Any insight on this matter will be appreciated. Also, why is log4j forcing me to mess with the thread context classloader? Why not let me pass in a class loader (and use a default one when I don't) instead of using the thread's one?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您似乎偶然发现了 log4j(以及 Apache Commons Logging 库)的主要问题,即它们在使用时发现正确的类加载器并与之交互非常困难。 此处有非常详细的解释,并附有示例;最重要的信息是,新日志框架 SLF4J 的主要驱动力之一就是消除这些问题完全。您可能想更换它,看看您的生活是否变得更轻松。
You appear to have stumbled upon the major problem with log4j (and the Apache Commons Logging library), namely that they have a ridiculously hard time discovering and interacting with the right classloaders as they're being used. There's a very dense explanation, complete with examples, here; the take-home message is that one of the primary driving forces for the new logging framework SLF4J was to eliminate these issues entirely. You might want to swap it in and see if your life is made any easier.