parallelStream() 使用 JAXB-API 导致 ClassNotFoundException

Eth*_*roy 22 java jaxb classloader java-11

在我们的应用程序中,我们有时会遇到以下异常:

javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
 - with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
Run Code Online (Sandbox Code Playgroud)

我们已经发现只有当我们使用时才会发生这种情况,Collection.parallelStream()但如果我们使用Collection.stream().

我们看到 JAXBThread.currentThread().getContextClassLoader()用于加载类。我们还看到,当使用 时parallelStream(),执行我们命令的线程使用不同的类加载器。有时是org.apache.catalina.loader.WebappClassLoader,有时是jdk.internal.loader.ClassLoaders.AppClassLoader

现在看来,AppClassLoader不知道 JAXB 依赖项,而WebappClassLoader

我们正在使用 Java 11 和以下 Maven 依赖项:

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

知道有什么问题吗?怎么可能,AppClassLoader不知道我们的依赖关系?

小智 8

我们有不同的经历。问题是 tomcat 的类加载器与每次战争都不同(我认为我们在 Spring Boot 应用程序中看到了类似的东西;void-main 与 application-runner 的不同)。

无论哪种方式,问题在于“引导”类加载器无权访问您的应用程序中的 jar - 因此没有 jaxb。因此,如果您启动了一个线程(如ForkJoinPoolThreadfromStreamXX.parallel()ForkJoinPool.commonThreadPool()),那么这些线程将来自引导类加载器,而不是您的应用程序的类加载器。因此,如果您的后台任务第一次加载 JAXB,它们将运行getClass().getContextClassLoader().getResourceAsStream("xxxx")并且不会找到资源。

我们的解决方案是在显式线程池中启动所有后台任务,并有一个显式线程池工厂。线程池工厂从调用线程(由 war 或 spring-boot 上下文初始化的线程)捕获类加载器)。这个类加载器将有 jaxb 和朋友......所以现在从这个线程池工厂启动的每个线程都有一个明确的thr.setContextClassLoader(globalCL);......

问题已解决(通过 hack)


Sac*_*mar 0

我遇到了类似的问题,我得到了 ClassNotFoundException for org.apache.xerces.parsers.SAXParser. 我已经删除/排除了xercesImplxml-api、的依赖关系xmlbeansxmlschema-core并且问题已解决,但我不知道它为什么以及如何工作。促使我删除这些 jar 的是 XMLReaderFactory,它存在于JDK 8和中xml-api