【问题标题】:parallelStream() causing ClassNotFoundException with JAXB-API使用 JAXB-API 导致 ClassNotFoundException 的 parallelStream()
【发布时间】:2019-08-22 11:32:56
【问题描述】:

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

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]

我们已经发现只有在使用 Collection.parallelStream() 时才会发生这种情况,而在使用 Collection.stream() 时不会发生这种情况。

我们看到 JAXB 使用 Thread.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>

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

【问题讨论】:

    标签: java jaxb classloader java-11


    【解决方案1】:
    【解决方案2】:

    您正在从某个地方加载旧版本的 JAXB。在 Java 11 中,上下文工厂应该是: com.sun.xml.bind.v2.ContextFactory

    而不是

    com.sun.xml.internal.bind.v2.ContextFactory
    

    https://github.com/eclipse-ee4j/jaxb-api/issues/78

    尝试执行以下操作: 将您的激活包升级到 1.2 。不确定它与它有什么关系,但由于您使用的是 java 11,我建议使用激活 1.2

    而不是插入 jaxB 的 glassfish 实现。使用以下依赖项:

    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.3.0</version>
    </dependency>
    

    或者

    <dependency>
        <groupId>jakarta.xml.bind</groupId>
        <artifactId>jakarta.xml.bind-api</artifactId>
        <version>2.3.3</version>
    </dependency>
    

    如果这仍然不起作用,请退回到我写给你的第一件事:

    正确的上下文工厂是:

    com.sun.xml.bind.v2.ContextFactory
    

    那里加载了一个旧版本。

    【讨论】:

      【解决方案3】:

      这个类 @987654321@ 之前在文件 rt.jar 直到 java 8 中可用,然后从 jdk 中删除。


      可能的原因:您的应用程序使用了一些库,该库使用了一些旧库,它需要java 8,其中包含xml.bind,它是从jdk 11 中删除的。


      解决方法:您可以简单地从jdk 8 复制文件/usr/lib/jvm/jre-1.8.0-openjdk/lib/rt.jar,修剪其内容,仅保存com.sun.xml.internal.bind.* 包。然后将此jar 放入您的tomcat/lib 文件夹中。

      【讨论】:

        【解决方案4】:

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

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

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

        问题已解决(通过 hack)

        【讨论】:

          【解决方案5】:

          我遇到了类似的问题,我得到 org.apache.xerces.parsers.SAXParser 的 ClassNotFoundException。我已经删除/排除了 xercesImpl,xml-api,xmlbeans,xmlschema-core 的依赖项,问题已解决,但我不知道它为什么以及如何工作。促使我删除这些 jar 的是 XMLReaderFactory,它出现在 JDK 8xml-api 中。

          【讨论】:

            猜你喜欢
            • 2021-12-30
            • 2012-04-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-06-23
            • 2019-12-12
            • 2015-04-19
            • 1970-01-01
            相关资源
            最近更新 更多