Nic*_*lai 19 java tomcat java-9 java-10
TLDR:在Java 9/10上,Tomcat中的Web应用程序无法访问JAXB,即使它的引用实现存在于类路径中.
编辑:不,这不是如何解决java.lang.NoClassDefFoundError:Java 9中的javax/xml/bind/JAXBException的副本- 正如您在"我尝试过的内容"部分所述,我已经尝试了所提出的解决方案.
我们有一个在Tomcat上运行的Web应用程序,它依赖于JAXB.在我们迁移到Java 9期间,我们选择将JAXB参考实现添加为常规依赖项.
从具有嵌入式Tomcat的IDE启动应用程序时,一切正常,但在真正的Tomcat实例上运行时,我收到此错误:
Caused by: java.lang.RuntimeException: 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]
at [... our-code ...]
Caused by: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:278) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.find(ContextFinder.java:421) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
at [... our-code ...]
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[?:?]
at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190) ~[?:?]
at java.lang.ClassLoader.loadClass(ClassLoader.java:499) ~[?:?]
at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:122) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:155) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:276) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.find(ContextFinder.java:421) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
at [... our-code ...]
Run Code Online (Sandbox Code Playgroud)
注意:
在模块路径或类路径上找不到JAXB-API的实现.
这些是相关文件webapps/$app/WEB-INF/lib:
jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?
CLASSPATH也许将JAR添加到Tomcat的类路径中会有帮助setenv.sh吗?
CLASSPATH=
.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar:
.../webapps/$app/WEB-INF/lib/jaxb-impl-2.3.0.jar:
.../webapps/$app/WEB-INF/lib/jaxb-core-2.3.0.jar:
.../webapps/$app/WEB-INF/lib/javax.activation-1.2.0.jar
Run Code Online (Sandbox Code Playgroud)
不:
Caused by: javax.xml.bind.JAXBException: ClassCastException: attempting to cast
jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class to
jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class.
Please make sure that you are specifying the proper ClassLoader.
at javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:157) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:300) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:286) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.find(ContextFinder.java:409) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
at de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.initializeCommandExtractor(DefaultWmsRequestFactory.java:103) ~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]
at de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.lambda$new$0(DefaultWmsRequestFactory.java:87) ~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]
Run Code Online (Sandbox Code Playgroud)
这显然是同一个类,显然它已被两个类加载器加载.我怀疑系统类加载器和应用程序的类加载器,但为什么加载JAXBContext被委托给系统类加载器一次但不总是?在程序运行时,几乎看起来app的类加载器的委托行为发生了变化.
我真的不想添加java.xml.bind,但我还是尝试了将它添加到catalina.sh:
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-modules=java.xml.bind"
Run Code Online (Sandbox Code Playgroud)
但是也不起作用:
Caused by: java.lang.ClassCastException:
java.xml.bind/com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl
cannot be cast to com.sun.xml.bind.v2.runtime.JAXBContextImpl
at [... our-code ...]
Run Code Online (Sandbox Code Playgroud)
除了不同的类和堆栈跟踪之外,这与之前发生的情况一致:类JAXBContextImpl已经加载了两次,一次是从java.xml.bind(必须是系统类加载器)和另一次加载(我假设是应用程序的来自JAR的装载机).
搜索Tomcat的bug数据库,我找到了#62559.这可能是同样的错误吗?
lib根据Tomcat用户邮件列表给出的建议,我将JAXB JAR添加到Tomcat的CATALINA_BASE/lib目录中,但是得到了与应用程序的lib文件夹中相同的错误.
Nic*_*lai 10
首先是一些随机事实:
JAXBContext::newInstance将使用线程的上下文类加载器 - 即使你调用也是如此newInstance(Class...)(可能会错误地认为它使用提供的类实例的加载器)所以这就是Java 8上发生的事情:
Java 9进入 - 钢琴停止播放,每个人都放下苏格兰威士忌:
解决方案是确保JAXB使用正确的类加载器.我们知道三种方式:
Thread.getCurrentThread().setContextClassLoader(this.getClass().getClassLoader());但这不是一个好主意JAXBContext::newInstance(来自Java EE 7的Javadoc),它也接受类加载器并传递正确的加载器,尽管这需要一些重构我们使用了第三个选项,并针对包接受变体进行了重构JAXBContext::newInstance.琐碎的工作,但修复了问题.
用户curlals提供了关键信息,但删除了他们的答案.我希望不是因为我要求进行一些编辑.所有的信用/业力应该归他们所有!@curlals:如果你恢复并编辑你的答案,我会接受并赞成它.
尝试以下及其依赖项.查看Maven存储库以获取最新版本.
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.0.1</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
它还包含Java Service Loader描述符.请参阅在Java 9+中使用JAXB
我在代码的特定部分使用 Spring Boot(版本 2.2.6)和嵌入式 Tomcat 时遇到了这个问题,其中我使用了CompletableFuture. 该代码与 Java 8 完美配合,并在 Java 12 中通过了相关单元测试。仅当使用 Java 11 或 12 在 Tomcat 中执行应用程序时,才会出现该问题。
调试问题我发现问题与'sClassLoader内部使用了不同的事实有关。CompletableFutureRunner
// here Thread.currentThread().getContextClassLoader().getClass()
// returns org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader
return CompletableFuture.runAsync(() -> {
// here returns jdk.internal.loader.ClassLoaders$AppClassLoader
});
Run Code Online (Sandbox Code Playgroud)
第二个ClassLoader是无法加载 JAXB 类。这种行为似乎仅在 Java 9+ 中出现,实际上在 Java 9ForkJoinPool.common()返回Executor带有ClassLoadermain 的a 之前Thread,但在 Java 9 之后它返回带有 system 的执行器ClassLoader。
由于该CompletableFuture.runAsync()方法接受Executor第二个参数,因此可以Executor在代码中设置所需的参数。这是一个可能的解决方案的示例。
首先,定义一个适当的ForkJoinWorkerThreadFactory:
public class JaxbForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory {
private final ClassLoader classLoader;
public JaxbForkJoinWorkerThreadFactory() {
classLoader = Thread.currentThread().getContextClassLoader();
}
@Override
public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
ForkJoinWorkerThread thread = new JaxbForkJoinWorkerThread(pool);
thread.setContextClassLoader(classLoader);
return thread;
}
private static class JaxbForkJoinWorkerThread extends ForkJoinWorkerThread {
private JaxbForkJoinWorkerThread(ForkJoinPool pool) {
super(pool);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后将Executorusing 该工厂传递给该runAsync()方法:
return CompletableFuture.runAsync(() -> {
// now you have the right ClassLoader here
}, getJaxbExecutor());
private ForkJoinPool getJaxbExecutor() {
JaxbForkJoinWorkerThreadFactory threadFactory = new JaxbForkJoinWorkerThreadFactory();
int parallelism = Math.min(0x7fff /* copied from ForkJoinPool.java */, Runtime.getRuntime().availableProcessors());
return new ForkJoinPool(parallelism, threadFactory, null, false);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6575 次 |
| 最近记录: |