在 Java 1.6 中为 JAX-WS 提供不同版本的 JAXB

2022-09-01 09:46:00

我有一个第三方jar,它附带了一个jaxb-impl.jar并将其包含在其清单类路径中。问题是,似乎提供你自己的 JAXB 版本(不管它可能是哪个版本)似乎破坏了 JAX-WS 中的 SoapFaultBuilder。

根据非官方的 JAXB 指南,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 时,无论是在类路径上还是在背书的 lib 中,我都会得到以下堆栈跟踪:

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

发生异常的原因是 com.sun.xml.bind.v2.runtime.JAXBContextImpl 从 my jaxb-impl 扩展了 com.sun.xml.bind.api.JAXBRIContext 而不是 com.sun.xml.internal.bind.api.JAXBRIContext(请注意包层次结构中缺少的“内部”子包)。

此外,根据非官方的 JAXB 指南,他们说你需要使用背书的 lib 才能正确地覆盖 JAXB 的版本。事实证明,SOAPFaultBuilder 使用 JAXBContext.newInstance() 在类路径中搜索名为 的文件,然后根据文件中指定的类名手动加载(并自反地创建)JAXBContext。所以这没关系 - 类路径或背书的lib给你相同的行为。/META-INF/services/javax.xml.bind.JAXBContext

一种解决方法是添加到命令行,这会导致 JAXBContext.newInstance() 忽略类路径上的文件并手动指定 JAXB 的内置版本。另一种解决方法是简单地不指定您自己的 JAXB 并使用 JDK 中内置的版本,但从非官方的 JAXB 指南中可以看出,Sun 设计此系统是为了能够处理提供您自己的 JAXB 实现。有没有人能够成功地提供 JAXB 的一个版本,但仍然能够成功地捕获错误消息?(只要 Web 服务没有生成任何错误,一切对我来说都很好)。-Djavax.xml.bind.JAXBContext=com.sun.xml.internal.bind.v2.ContextFactory/META-INF/services/javax.xml.bind.JAXBContext


答案 1

我被困在这个问题上,但能够通过设置系统属性来使用此论坛问答中列出的“解决方法”,如下所示:

System.setProperty("javax.xml.bind.JAXBContext", 
                   "com.sun.xml.internal.bind.v2.ContextFactory"); 

答案 2

此问题的症结在于 JAXB API 中存在更改,您尝试使用的运行时实现与 JDK 捆绑的 JAXB API 版本不匹配。

为了使用不同的版本,您应该将 jaxb-api.jarjaxws-api.jar 的对应版本复制到一个背书的 lib 中(例如 )。%JAVA_HOME%\lib\endorsed

非官方 JAXB 指南的第 7.1.2 节中给出了完整的选项列表

将实现 jar(例如 jaxb-impl.jar)复制到背书的 lib 中是错误的,这些应该只是在你的类路径上。

另请注意,如果您尝试使用较新版本的 jaxb 而不同时包含兼容版本的 jaxws,则可能会遇到麻烦。这是因为旧的 jaxws 会尝试引用旧的 jaxb,所以如果你要改变一个,请确保同时执行这两项操作。(包中的堆栈跟踪涉及旧的 jax-ws 实现。即使是最新版本的Java仍然附带旧版本1 jaxb和jaxws apis)。com.sun.xml.internal.ws


推荐