我有一个附带 jaxb-impl.jar 的第三方 jar,并将其包含在其清单类路径中。问题是,似乎提供您自己的 JAXB 版本(无论它可能是哪个版本)似乎会破坏 JAX-WS 中的 SoapFaultBuilder。
根据Unofficial JAXB Guide,似乎 Sun 在将 JAXB 折叠到 JDK 时故意更改了包名,以避免与单机版本冲突。但是,JDK 附带的 SoapFaultBuilder(我相信是 JAX-WS 的一部分)显式依赖于新的内部包名称。如果您添加了独立的 JAXB jar(即使它与 JAXB 的版本号相同),这会导致它在构建错误消息时失败。
这是我的小测试用例:我做了一个微不足道的 Web 服务:
package wstest;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
//Service Endpoint Interface
@WebService
@SOAPBinding(style = Style.RPC)
public interface HelloWorld{
@WebMethod String getHelloWorldAsString(String name);
}
以及一个简单地抛出异常的实现。(因为问题只发生在 SOAPFaultBuilder 中):
package wstest;
import javax.jws.WebService;
//Service Implementation
@WebService(endpointInterface = "wstest.HelloWorld")
public class HelloWorldImpl implements HelloWorld{
@Override
public String getHelloWorldAsString(String name) {
//return "Hello World JAX-WS " + name;
throw new RuntimeException("Exception for: " + name);
}
}
还有一个发布 Web 服务的类:
package wstest;
import javax.xml.ws.Endpoint;
//Endpoint publisher
public class HelloWorldPublisher{
public static void main(String[] args) {
Endpoint.publish("http://localhost:9999/ws/hello", new HelloWorldImpl());
}
}
我运行 HelloWorldPublisher,然后针对它运行这个客户端:
package wstest;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class HelloWorldClient{
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:9999/ws/hello?wsdl");
//1st argument service URI, refer to wsdl document above
//2nd argument is service name, refer to wsdl document above
QName qname = new QName("http://wstest/", "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
System.out.println(hello.getHelloWorldAsString("Matt"));
}
}
这正确地吐出由 Web 服务引发的异常。但是,当我添加任何版本的 jaxb-impl.jar 时,无论是在类路径中还是在认可的库中,我都会得到以下堆栈跟踪:
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:107)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:107)
at $Proxy19.getHelloWorldAsString(Unknown Source)
at wstest.HelloWorldClient.main(HelloWorldClient.java:21)
Caused by: java.lang.ClassCastException: com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.api.JAXBRIContext
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.<clinit>(SOAPFaultBuilder.java:533)
... 5 more
发生异常是因为我的 jaxb-impl 中的 com.sun.xml.bind.v2.runtime.JAXBContextImpl 扩展了 com.sun.xml.bind.api.JAXBRIContext 而不是 com.sun.xml.internal.bind.api.JAXBRIContext (请注意包层次结构中缺少的“内部”子包)。
同样根据Unofficial JAXB Guide,他们说您需要使用认可的 lib 才能正确覆盖 JAXB 的版本。但事实证明,SOAPFaultBuilder 使用 JAXBContext.newInstance() 在类路径中搜索名为 的文件/META-INF/services/javax.xml.bind.JAXBContext
,然后根据文件中指定的类名手动加载(并反身地创建)一个 JAXBContext。所以没关系 - 类路径或认可的库给你同样的行为。
一种解决方法是添加-Djavax.xml.bind.JAXBContext=com.sun.xml.internal.bind.v2.ContextFactory
到命令行,这会导致 JAXBContext.newInstance() 忽略/META-INF/services/javax.xml.bind.JAXBContext
类路径中的文件并手动指定 JAXB 的内置版本。另一种解决方法是简单地不指定您自己的 JAXB 并使用 JDK 中内置的版本,但从非官方 JAXB 指南看来,Sun 设计该系统是为了能够处理提供您自己的 JAXB 实现。有没有人能够成功地提供一个 JAXB 版本并且仍然能够成功地捕获故障消息?(只要 Web 服务没有产生任何故障,对我来说一切都运行良好)。