parallelStream()使用JAXB-API导致ClassNotFoundException [英] parallelStream() causing ClassNotFoundException with JAXB-API

查看:88
本文介绍了parallelStream()使用JAXB-API导致ClassNotFoundException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In our application we sometimes get the following exception:

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()

We already found out that this happens only if we use Collection.parallelStream() but not if we use Collection.stream().

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

We saw that JAXB uses Thread.currentThread().getContextClassLoader() for loading classes. We also saw, that when using parallelStream(), the threads for executing our command are using different class loaders. Sometimes it's a org.apache.catalina.loader.WebappClassLoader, and sometimes it's a jdk.internal.loader.ClassLoaders.AppClassLoader.

现在看来, AppClassLoader 不了解JAXB依赖关系,而 WebappClassLoader

And it now seems, that the AppClassLoader does not know about the JAXB dependencies, whereas the WebappClassLoader does.

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

We are using Java 11 and the following Maven dependencies:

<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 怎么可能不了解我们的依赖关系?

Any idea what could be wrong? How can it be, that the AppClassLoader does not know about our dependencies?

推荐答案

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

We had a different experience. The issue was that tomcat has a different classloader than each war (I think we're seeing something similar with spring boot apps; void-main was different than that of application-runner)..

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

Either way, the issue is that the "bootstrap" classloader doesn't have access to the jars in your application - and thus NO jaxb. So if you ever launch a thread (like ForkJoinPoolThread from StreamXX.parallel() or ForkJoinPool.commonThreadPool()) then THOSE threads will be from the bootstrap classloader, NOT your application's classloader.. So if the FIRST time your background task loads JAXB, they will run getClass().getContextClassLoader().getResourceAsStream("xxxx") and will not find the resource.

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

Our solution was to launch all background tasks in an explicit thread-pool and have an explicit thread-pool factory.. The thread-pool-factory captures the classloader from the invoking thread (the one initialized by the war or the spring-boot context). This classloader WILL have jaxb and friends.. So now each thread launched from this thread-pool factory has an explicit thr.setContextClassLoader(globalCL);....

问题(通过黑客入侵)

这篇关于parallelStream()使用JAXB-API导致ClassNotFoundException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆