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 ...]
注意:
在模块路径或类路径上找不到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
这里发生了什么?
我尝试了什么
将JAR添加到Tomca的 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
不:
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]
这显然是同一个类,显然它已被两个类加载器加载.我怀疑系统类加载器和应用程序的类加载器,但为什么加载JAXBContext
被委托给系统类加载器一次但不总是?在程序运行时,几乎看起来app的类加载器的委托行为发生了变化.
添加模块
我真的不想添加java.xml.bind,但我还是尝试了将它添加到catalina.sh
:
JDK_JAVA_OPTIOnS="$JDK_JAVA_OPTIONS --add-modules=java.xml.bind"
但是也不起作用:
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 ...]
除了不同的类和堆栈跟踪之外,这与之前发生的情况一致:类JAXBContextImpl
已经加载了两次,一次是从java.xml.bind(必须是系统类加载器)和另一次加载(我假设是应用程序的来自JAR的装载机).
搜索错误
搜索Tomcat的bug数据库,我找到了#62559.这可能是同样的错误吗?
将JAR添加到Tomcat中 lib
根据Tomcat用户邮件列表给出的建议,我将JAXB JAR添加到Tomcat的CATALINA_BASE/lib
目录中,但是得到了与应用程序的lib文件夹中相同的错误.
1> Nicolai..:
分析
首先是一些随机事实:
如果没有给出类加载器,在查找JAXB实现时JAXBContext::newInstance
将使用线程的上下文类加载器 - 即使你调用也是如此newInstance(Class...)
(可能会错误地认为它使用提供的类实例的加载器)
Tomcat构建了一个小型的类加载器层次结构,以将Web应用程序彼此分开
不依赖于java.xml.bind模块,在Java 9中,引导程序或系统类加载器不加载JAXB类
所以这就是Java 8上发生的事情:
我们不会将类加载器传递给JAXB(oops),因此它使用线程的上下文类加载器
我们的猜想是Tomcat没有显式设置上下文类加载器,所以最终会加载Tomcat:系统类加载器
这是花花公子,因为系统类加载器看到整个JDK,因此包含其中的JAXB实现
Java 9进入 - 钢琴停止播放,每个人都放下苏格兰威士忌:
我们将JAXB添加为常规依赖项,因此它由Web应用程序的类加载器加载
就像在Java 8上一样,JAXB搜索系统类加载器,但是那个人看不到应用程序的加载器(只是反过来)
JAXB无法找到实现并且无法实现
解
解决方案是确保JAXB使用正确的类加载器.我们知道三种方式:
打电话Thread.getCurrentThread().setContextClassLoader(this.getClass().getClassLoader());
但这不是一个好主意
创建一个上下文解析器,但这需要JAX-WS,这就像用另一个邪恶替换一个邪恶
使用接受包的接受变体JAXBContext::newInstance
(来自Java EE 7的Javadoc),它也接受类加载器并传递正确的加载器,尽管这需要一些重构
我们使用了第三个选项,并针对包接受变体进行了重构JAXBContext::newInstance
.琐碎的工作,但修复了问题.
注意
用户curlals提供了关键信息,但删除了他们的答案.我希望不是因为我要求进行一些编辑.所有的信用/业力应该归他们所有!@curlals:如果你恢复并编辑你的答案,我会接受并赞成它.
2> Philippe Mar..:
尝试以下及其依赖项.查看Maven存储库以获取最新版本.
org.glassfish.jaxb
jaxb-runtime
2.3.0.1
它还包含Java Service Loader描述符.请参阅在Java 9+中使用JAXB