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: