parallelStream() 导致 ClassNotFoundException with JAXB-API

2022-09-01 23:50:59

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

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]

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

我们看到 JAXB 用于加载类。我们还看到,当使用 时,用于执行命令的线程使用不同的类装入器。有时它是一个 ,有时它是一个 .Thread.currentThread().getContextClassLoader()parallelStream()org.apache.catalina.loader.WebappClassLoaderjdk.internal.loader.ClassLoaders.AppClassLoader

现在看来,他们不知道 JAXB 的依赖关系,而他们却知道。AppClassLoaderWebappClassLoader

我们使用的是 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


答案 1

我们有不同的经历。问题在于tomcat的类加载器与每次战争都不同(我认为我们在spring boot应用程序上看到了类似的东西;void-main与应用程序运行器不同)。

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

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

问题已解决(通过黑客攻击)


答案 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的玻璃鱼实现。使用以下依赖项:

<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

在某处装载了一个旧的vesion。


推荐