背景
使用了阿里云的MNS服务的sdk,将项目从 Java8 升级到 Java17,MNS拉取消息时出现该报错。
com.aliyun.mns.common.ClientException: Cannot invoke "java.lang.reflect.Method.invoke(Object, Object[])" because "com.sun.xml.bind.v2.runtime.reflect.opt.Injector.defineClass" is null
at com.aliyun.mns.common.http.ExceptionResultParser.parse(ExceptionResultParser.java:66) ~[aliyun-sdk-mns-1.1.10.jar:na]
at com.aliyun.mns.common.http.ExceptionResultParser.parse(ExceptionResultParser.java:31) ~[aliyun-sdk-mns-1.1.10.jar:na]
at com.aliyun.mns.common.http.HttpCallback.handleResult(HttpCallback.java:165) ~[aliyun-sdk-mns-1.1.10.jar:na]
at com.aliyun.mns.common.http.HttpCallback.buildResponseMessage(HttpCallback.java:138) ~[aliyun-sdk-mns-1.1.10.jar:na]
at com.aliyun.mns.common.http.HttpCallback.completed(HttpCallback.java:99) ~[aliyun-sdk-mns-1.1.10.jar:na]
at com.aliyun.mns.common.http.HttpCallback.completed(HttpCallback.java:44) ~[aliyun-sdk-mns-1.1.10.jar:na]
at org.apache.http.concurrent.BasicFuture.completed(BasicFuture.java:122) ~[httpcore-4.4.13.jar:4.4.13]
......
分析
从 Java8 升级到 Java17 后,javax 的相关内容都已经从 JDK 中移除,所以推测该问题为 JAXB 实现找不到。通过阿里云的 MNS文档 找到了相关提示内容
如果您使用的Java版本是Java 9及以上的版本,则需要添加以下JAXB相关依赖。
<dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.9</version> </dependency>
根据该提示添加 runtimeOnly("org.glassfish.jaxb:jaxb-runtime:2.3.9")
后,部署测试环境测试确实报错消失,但是更新到正式环境后却再次出现相同报错。
通过debug找到了JAXB在运行时查找实现类的源码位置 javax.xml.bind.ContextFinder#find
,发现可以通过系统属性 javax.xml.bind.JAXBContextFactory
或 javax.xml.bind.context.factory
直接指定运行时的JAXB工厂实现类。所以解决方案可以是添加一个系统属性来指定JAXB的工厂实现类。
解决
为了避免和原来的实现冲突,这里我使用了 moxy 库来作为JAXB的实现,moxy 是 EclipseLink 所使用的JAXB实现。
在 build.gradle
文件中添加
runtimeOnly("org.eclipse.persistence:org.eclipse.persistence.moxy:2.7.12")
然后在Java代码中添加
System.setProperty("javax.xml.bind.JAXBContextFactory", "org.eclipse.persistence.jaxb.JAXBContextFactory");
放在 main 方法的第一行即可。