Tomcat 自定义类加载器加载某些 jar 失败
Tomcat custom classloader failed to load some jars
我有一个具有上传功能的 Web 应用程序,它包括上传一个包含 java 应用程序(它可能包含多个依赖项)的包
为此,对于每个上传的应用程序,我正在创建一个自定义 classloader 来动态加载应用程序 classpath。
该解决方案工作正常,直到我在上传新应用程序时出现此错误:
javax.xml.stream.FactoryConfigurationError: Error creating stream factory: java.lang.ClassNotFoundException: de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl
我已验证我上传的包中包含 staxon,并且还验证了我的自定义 classloader 可以加载 class:
Object a=Class.forName("de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl",true , classLoader).newInstance();
那么,为什么会出现这个例外,而且是专门针对这个 jar 的?
当您调用 Class.forName(className, initialize, classLoader)
时,您要求 JVM 使用该自定义 class 加载程序加载 class。如果您不指定 ClassLoader
——例如仅通过调用 Class.forName(className)
,那么 JVM 将使用线程的 ClassLoader
,即 "context ClassLoader"。每个线程都有其中之一,您的代码可以稍微轻松地 更改 上下文 classloader。
如果您有一些代码导致 class 被加载 -- 像这样:
MyClass foo = new MyClass();
MyClass
class 将由 ClassLoader
加载(如果尚未加载)。如果尚未加载,则无法调用构造函数并提供 ClassLoader
来加载 class。在这种情况下,线程的上下文 ClassLoader
是 user.
此外,如果您调用了一些您无法控制的代码,该代码可以通过多种方式导致其他 classes 被加载,并且由于您无法控制该代码,线程的上下文 ClassLoader
也将被使用。
那么,如何设置线程的上下文ClassLoader
?简单:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
ClassLoader myClassLoader = ...; // You figure this out
try
{
Thread.currentThread().setContextClassLoader(myClassLoader);
// Do work that requires your ClassLoader to work
}
finally
{
// Always restore the previous CCL after work is done
Thread.currentThread().setContextClassLoader(cl);
}
您需要做的另一件事是确保您的自定义 ClassLoader
将任何 class 加载请求委托给父级 ClassLoader
。那个父 ClassLoader
可能应该是 ClassLoader
如果您不尝试使用自己的父 ClassLoader
:线程的上下文 ClassLoader
.
所以,您可能需要如下内容:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
ClassLoader myClassLoader = new MyClassLoader(cl); // Try 'cl' before your custom class loading
try
{
Thread.currentThread().setContextClassLoader(myClassLoader);
// Do work that requires your ClassLoader to work
}
finally
{
// Always restore the previous CCL after work is done
Thread.currentThread().setContextClassLoader(cl);
}
您可以在以下几个参考资料中找到有关 ClassLoader
的更多信息,特别是 TCCL:
- http://docs.oracle.com/javase/jndi/tutorial/beyond/misc/classloader.html
- Difference between thread's context class loader and normal classloader
- http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html(也参考了之前的参考资料)
- http://njbartlett.name/2012/10/23/dreaded-thread-context-classloader.html
我有一个具有上传功能的 Web 应用程序,它包括上传一个包含 java 应用程序(它可能包含多个依赖项)的包
为此,对于每个上传的应用程序,我正在创建一个自定义 classloader 来动态加载应用程序 classpath。 该解决方案工作正常,直到我在上传新应用程序时出现此错误:
javax.xml.stream.FactoryConfigurationError: Error creating stream factory: java.lang.ClassNotFoundException: de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl
我已验证我上传的包中包含 staxon,并且还验证了我的自定义 classloader 可以加载 class:
Object a=Class.forName("de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl",true , classLoader).newInstance();
那么,为什么会出现这个例外,而且是专门针对这个 jar 的?
当您调用 Class.forName(className, initialize, classLoader)
时,您要求 JVM 使用该自定义 class 加载程序加载 class。如果您不指定 ClassLoader
——例如仅通过调用 Class.forName(className)
,那么 JVM 将使用线程的 ClassLoader
,即 "context ClassLoader"。每个线程都有其中之一,您的代码可以稍微轻松地 更改 上下文 classloader。
如果您有一些代码导致 class 被加载 -- 像这样:
MyClass foo = new MyClass();
MyClass
class 将由 ClassLoader
加载(如果尚未加载)。如果尚未加载,则无法调用构造函数并提供 ClassLoader
来加载 class。在这种情况下,线程的上下文 ClassLoader
是 user.
此外,如果您调用了一些您无法控制的代码,该代码可以通过多种方式导致其他 classes 被加载,并且由于您无法控制该代码,线程的上下文 ClassLoader
也将被使用。
那么,如何设置线程的上下文ClassLoader
?简单:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
ClassLoader myClassLoader = ...; // You figure this out
try
{
Thread.currentThread().setContextClassLoader(myClassLoader);
// Do work that requires your ClassLoader to work
}
finally
{
// Always restore the previous CCL after work is done
Thread.currentThread().setContextClassLoader(cl);
}
您需要做的另一件事是确保您的自定义 ClassLoader
将任何 class 加载请求委托给父级 ClassLoader
。那个父 ClassLoader
可能应该是 ClassLoader
如果您不尝试使用自己的父 ClassLoader
:线程的上下文 ClassLoader
.
所以,您可能需要如下内容:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
ClassLoader myClassLoader = new MyClassLoader(cl); // Try 'cl' before your custom class loading
try
{
Thread.currentThread().setContextClassLoader(myClassLoader);
// Do work that requires your ClassLoader to work
}
finally
{
// Always restore the previous CCL after work is done
Thread.currentThread().setContextClassLoader(cl);
}
您可以在以下几个参考资料中找到有关 ClassLoader
的更多信息,特别是 TCCL:
- http://docs.oracle.com/javase/jndi/tutorial/beyond/misc/classloader.html
- Difference between thread's context class loader and normal classloader
- http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html(也参考了之前的参考资料)
- http://njbartlett.name/2012/10/23/dreaded-thread-context-classloader.html