parallelStream()使用JAXB-API导致ClassNotFoundException

时间:2019-04-01 10:17:15

标签: 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]

我们已经发现只有在使用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怎么可能不了解我们的依赖关系?

5 个答案:

答案 0 :(得分:9)

我们有不同的经历。问题在于,tomcat的类加载器与每次战争都不一样(我认为我们在Spring Boot应用程序中看到了类似的东西; void-main与应用程序运行器的情况有所不同)。.

无论哪种方式,问题都是“ bootstrap”类加载器无法访问应用程序中的jar,因此没有jaxb。因此,如果您曾经启动线程(例如来自ForkJoinPoolThreadStreamXX.parallel()的{​​{1}},那么这些线程将来自引导类加载器,而不是应用程序的类加载器。)因此,如果您是第一次使用背景任务加载JAXB,它们将运行ForkJoinPool.commonThreadPool()并且找不到资源。

我们的解决方案是在一个显式线程池中启动所有后台任务,并建立一个显式线程池工厂。线程池工厂从调用线程(由war或spring初始化的线程)中捕获类加载器。 -boot上下文)。该类加载器将具有jaxb和朋友。.因此,现在从该线程池工厂启动的每个线程都有一个显式的getClass().getContextClassLoader().getResourceAsStream("xxxx") ....

问题(通过黑客入侵)

答案 1 :(得分:1)

您正在从某处加载旧版本的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

在那儿有一个旧的装满的东西。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

此类com.sun.xml.internal.bind.v2.ContextFactory以前在文件rt.jar中可用,直到java 8,然后从jdk中删除。


可能的原因::您的应用程序使用了一些库,该库使用了一些旧库,期望使用java 8,其中包含xml.bind,该库已从{{1}中删除}。


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

答案 4 :(得分:0)