在我们的应用程序中,我们有时会得到以下异常:
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
怎么可能不知道我们的依赖关系?
我们有不同的经历。 问题是tomcat的类加载器与每场战争不同(我认为我们在春季启动应用程序中看到类似的东西;void-main与应用程序运行器不同)。
无论哪种方式,问题都是"引导"类加载器无法访问应用程序中的jar - 因此没有jaxb。因此,如果您曾经启动过一个线程(例如来自StreamXX.parallel()
或ForkJoinPool.commonThreadPool()
的线程ForkJoinPoolThread
),那么这些线程将来自引导类加载器,而不是应用程序的类加载器。因此,如果后台任务第一次加载 JAXB,它们将运行getClass().getContextClassLoader().getResourceAsStream("xxxx")
并且找不到资源。
我们的解决方案是在显式线程池中启动所有后台任务,并具有显式线程池工厂。线程池工厂从调用线程(由 war 或 spring-boot 上下文初始化的线程)捕获类加载器。这个类加载器将有jaxb和朋友。所以现在从这个线程池工厂启动的每个线程都有一个显式thr.setContextClassLoader(globalCL);
....
问题已解决(通过黑客攻击)
我遇到了类似的问题,我得到了 ClassNotFoundException fororg.apache.xerces.parsers.SAXParser
.我已经删除/排除了xercesImpl
、xml-api
、xmlbeans
、xmlschema-core
的依赖项,问题已经解决,尽管我不知道它为什么以及如何工作。促使我删除这些罐子的是XMLReaderFactory,它存在于JDK 8
和xml-api
中。
您正在从某处加载旧版本的 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 的玻璃鱼实现。使用以下依赖项:
<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
那里某处装着一个旧船。
JDK9 中引入了行为更改: JDK-8184335:发布说明:分叉/联接公共池线程返回系统类装入器作为其线程上下文类装入器(由于 JDK-8172726:ForkJoin 公共池保留对线程上下文类装入器的引用)。
另请参阅 jdk8 和 jdk11 中的不同类加载器。
>这个类com.sun.xml.internal.bind.v2.ContextFactory
以前在文件中可用,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
文件夹中。