Web 服务提示与技巧: JAX-RPC 与 JAX-WS 的比较,第 3 部分

服务端点接口

本系列的三篇文章重点关注于 Java™ API for XML-based RPC (JAX-RPC) 1.1 和 Java API for XML Web Services (JAX-WS) 2.0,本文是其中的第 3 部分,比较了从 Web 服务描述语言 (WSDL) 到服务端点接口 (SEI) 的映射。SEI 的概念是在 JAX-RPC 1.0 中首次引入的,在 JAX-WS 2.0 中保留了这个概念,并且添加了一些新的内容。本文将向您介绍其中的主要区别。

Russell Butek (butek@us.ibm.com), IT 专家, EMC

Russell Butek 是 IBM 的一名 SOA 和 Web 服务顾问。他曾是 IBM WebSphere Web 服务引擎的开发人员之一。他也是 JAX-RPC Java Specification Request (JSR) 专家组的成员。他参与了 Apache 的 AXIS SOAP 引擎的实现,并推动 AXIS 1.0 遵守 JAX-RPC。


developerWorks 投稿作者

Nicholas Gallardo (nlgallar@us.ibm.com), 软件工程师, EMC

Nick Gallardo 担任 IBM WebSphere 平台软件工程师,主要负责 Web 服务支持的各个方面。在此之前,他从事 IBM WebSphere 和 Tivoli 平台中其他方面的工作。Nick 于 2001 加入 IBM,此前他曾在德克萨斯州奥斯汀市两家不同的初创技术型公司从事开发工作。



2007 年 9 月 06 日

引言

从整体上看,Java API for XML-based RPC (JAX-RPC) 1.1 服务端点接口 (SEI) 和 Java API for XML Web Services (JAX-WS) 2.0 SEI 是非常相似的。本文将着重介绍它们之间的区别。尽管在结构上存在一定的区别,然而,提供反映 Web 服务契约的接口的目标是相同的。

比较 SEI 映射

清单 1 显示了一个简单的 HelloWorld Web 服务所使用的 WSDL。

清单 1. HelloWorld WSDL
  <?xml version="1.0" encoding="UTF-8"?>
      <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
      xmlns:tns="urn:helloWorld/sample/ibm/com"
      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="HelloWorld"
      targetNamespace="urn:helloWorld/sample/ibm/com">
  <wsdl:types>
    <xsd:schema targetNamespace="urn:helloWorld/sample/ibm/com"
        xmlns:tns="urn:helloWorld/sample/ibm/com"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:element name="hello">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="name" nillable="true" type="xsd:string" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="helloResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="response" nillable="true" type="xsd:string" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="helloRequestMsg">
    <wsdl:part element="tns:hello" name="helloParameters" />
  </wsdl:message>
  <wsdl:message name="helloResponseMsg">
    <wsdl:part element="tns:helloResponse" name="helloResult" />
  </wsdl:message>
  <wsdl:portType name="HelloWorld">
    <wsdl:operation name="hello">
      <wsdl:input message="tns:helloRequestMsg" name="helloRequest" />
      <wsdl:output message="tns:helloResponseMsg" name="helloResponse" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="HelloWorldBinding" type="tns:HelloWorld">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="hello">
      <soap:operation soapAction="urn:helloWorld/sample/ibm/com/hello" />
      <wsdl:input name="helloRequest">
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output name="helloResponse">
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="HelloWorldService">
    <wsdl:port name="port" binding="tns:HelloWorldBinding">
      <soap:address location="http://tempuri.org/" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

清单 2 显示了这个 WSDL 中 Java SEI 的 JAX-RPC 映射,其中不包含方法签名(稍后您将看到有关这些内容的细节)。

清单 2. JAX-RPC HelloWorld SEI
package com.ibm.samples;
public interface HelloWorld extends java.rmi.Remote {
...
}

清单 3 显示了用于相同的 WSDL 的 JAX-WS SEI。

清单 3. JAX-WS HelloWorld SEI
package com.ibm.samples.helloworld;

import javax.jws.WebService;

@WebService(name = "HelloWorld", targetNamespace = "urn:samples.ibm.com/HelloWorld")
public interface HelloWorld {
  ...
}

其中存在三处区别:

  • 包:目标命名空间是“urn:helloWorld/sample/ibm/com”。这两个映射都使用了类似域名的字符串,并且颠倒了其中元素的顺序。JAX-RPC 的映射在第一个斜杠处结束。JAX-WS 的映射则继续该字符串,在第一个斜杠后面继续添加信息。这两种规范都允许从自定义的命名空间到包的映射。
  • 注释:JAX-WS 要求所有 SEI 必须包括 @WebService 注释。正如本系列文章的第 1 部分中所介绍的,JAX-WS 支持 JSR-181 Web 服务元数据中定义的各种注释。
  • java.rmi.Remote:JAX-RPC SEI 扩展了 java.rmi.Remote 接口。而 JAX-WS 不需要这样做。

在继续学习操作映射的详细内容之前,关于 SEI 本身还有一处需要说明的地方。尽管 JAX-WS 提供对具有 SEI 的 Web 服务的支持,但是对于所有的服务来说,这并不是强制的。使用 JAX-WS,JavaBean 可以作为 Web 服务实现而自行部署,不需要像 JAX-RPC 那样要求 Bean 必须包括 SEI。所部署的没有 SEI 的 JAX-WS 服务将被认为具有隐式的 SEI


比较操作映射

现在您已经看到了这些接口,下面再对各种操作的映射进行比较。可以使用不同的方法来设计表示具有类似语义的 Web 服务的 WSDL 文档。文章 Which style of WSDL should I use? 提供了关于各种不同风格的 WSDL 文档的概述,以及如何确定哪一种风格更适合您。

现在,让我们来研究 JAX-RPC 和 JAX-WS 如何映射到每种 WSDL 风格。

研究 document/literal 包装模式

清单 1 中的 WSDL 使用了 document/literal 包装模式进行格式化。清单 4 和 5 是 JAX-RPC 和 JAX-WS 中对相同的包装操作的映射。请注意,JAX-WS 向方法中添加了 @RequestWrapper@ResponseWrapper 注释。这些注释为用作操作包装器的元素和为这些包装器元素生成的任何 Java Bean 提供了附加的元数据。这些注释都是可选的。

清单 4. JAX-RPC 完整的 HelloWorld SEI
package com.ibm.samples;

public interface HelloWorld extends java.rmi.Remote {
    public java.lang.String hello(java.lang.String name) throws java.rmi.RemoteException;
}
清单 5. JAX-WS 完整的 HelloWorld SEI
package com.ibm.samples.helloworld;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;

@WebService(name = "HelloWorld", targetNamespace = "urn:samples.ibm.com/HelloWorld")
public interface HelloWorld {

    @WebMethod(action = "urn:samples.ibm.com/HelloWorld/hello")
    @WebResult(name = "response", targetNamespace = "")
    @RequestWrapper(localName = "hello",
        targetNamespace = "urn:samples.ibm.com/HelloWorld", 
        className = "com.ibm.samples.helloworld.Hello")
    @ResponseWrapper(localName = "helloResponse", 
        targetNamespace = "urn:samples.ibm.com/HelloWorld", 
        className = "com.ibm.samples.helloworld.HelloResponse")
    public String hello(
        @WebParam(name = "name", targetNamespace = "")
        String name);
}

正如您所看到的,JAX-WS 映射包含大量的注释,但是当您仔细研究根签名时会发现,唯一的区别是JAX-RPC 方法可以引发 java.rmi.RemoteException,而 JAX-WS 方法则不行。

研究 document/literal 模式

JAX-RPC 和 JAX-WS 都支持 document/literal 映射操作,但不支持包装映射操作。要在 HelloWorld 示例中实现这一点,您需要删除表示操作名的包装器元素。清单 6 显示了这个 WSDL 文档中相关的部分,并与清单 1 中的 WSDL 进行对比。

清单 6. 文档/文本 WSDL
<wsdl:types>
  <xsd:schema targetNamespace="urn:helloWorld/sample/ibm/com"
   xmlns:tns="urn:helloWorld/sample/ibm/com"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:element name="hello" type="xsd:string"/>
      <xsd:element name="helloResponse" type="xsd:string"/>
  </xsd:schema>
</wsdl:types>

<wsdl:message name="helloRequestMsg">
  <wsdl:part element="tns:hello" name="helloParameters" />
</wsdl:message>

<wsdl:message name="helloResponseMsg">
  <wsdl:part element="tns:helloResponse" name="helloResult" />
</wsdl:message>

现在,让我们来研究用于这个新的 WSDL 的 Java 映射。清单 7 和 8 分别显示了 JAX-RPC 和 JAX-WS 映射。JAX-RPC 映射是非常类似的!唯一的区别是参数名称。与前面的情况一样,如果不考虑注释,JAX-RPC 映射和 JAX-WS 映射之间没有什么本质的区别。

清单 7. JAX-RPC 文档/文本映射
public interface HelloWorld extends java.rmi.Remote {
    public java.lang.String hello(java.lang.String helloParameters) 
        throws java.rmi.RemoteException;
}
清单 8. JAX-WS 文档/文本映射
@WebService(name = "HelloWorld", targetNamespace = "urn:helloWorld/sample/ibm/com")
@SOAPBinding(parameterStyle = ParameterStyle.BARE)
public interface HelloWorld {

    @WebMethod(action = "urn:helloWorld/sample/ibm/com/hello")
    @WebResult(name = "helloResponse", 
        targetNamespace = "urn:helloWorld/sample/ibm/com", 
        partName = "helloResult")
    public String hello(
        @WebParam(name = "hello", 
            targetNamespace = "urn:helloWorld/sample/ibm/com", 
            partName = "helloParameters")
        String helloParameters);

}

请注意,对于 JAX-WS,没有 @RequestWrapper@ResponseWrapper 注释。另外请注意,在接口级别上还出现了一个新的注释,@SOAPBinding。这个注释提供了关于参数风格的信息。如果没有这个注释,parameterStyle 属性的缺省值为 wrapped,应该与清单 1 中的 WSDL 类似。

研究 RPC/literal 模式

下面的示例与前两个示例有一些不同。对于 RPC/literal 风格的 WSDL,这些部分定义为类型而不是元素。清单 9 包含了相关的 WSDL 差别。

清单 9. RPC/文本 WSDL 更改
<wsdl:types/>

<wsdl:message name="helloRequestMsg">
  <wsdl:part name="helloParameters" type="xsd:string"/>
</wsdl:message>

<wsdl:message name="helloResponseMsg">
  <wsdl:part name="helloResult" type="xsd:string"/>
</wsdl:message>

清单 10 和 11 中的 Java 映射反映了对 WSDL 的更改。同样地,当去掉注释后,可以得到完全相同的映射。

清单 10. JAX-RPC RPC/文本映射
public interface HelloWorld extends java.rmi.Remote {
    public java.lang.String hello(java.lang.String helloParameters)
        throws java.rmi.RemoteException;
}
清单 11. JAX-WS RPC/文本映射
@WebService(name = "HelloWorld", targetNamespace = "urn:helloWorld/sample/ibm/com")
@SOAPBinding(style = Style.RPC)
public interface HelloWorld {

    @WebMethod(action = "urn:helloWorld/sample/ibm/com/hello")
    @WebResult(name = "helloResult", partName = "helloResult")
    public String hello(
        @WebParam(name = "helloParameters", partName = "helloParameters")
        String helloParameters);

}

将这个 JAX-WS 接口与前面的进行比较,您将看到保留了 @SOAPBinding 注释,但是现在它不是表示参数风格,而是表示 WSDL 风格。

研究 RPC/encoded 模式

对于 RPC/encoded 风格的操作,无法进行比较。JAX-WS 不支持包含编码的 数据表示的 WSDL 文档的任何映射。这是因为 JAX-WS 与 WS-I 的 Basic Profile 1.1 的兼容性原因,其中不允许使用编码的 WSDL 文档。有时候的确需要构建 RPC/encoded Web 服务,在这种情况下您应该坚持使用 JAX-RPC 映射,但是如果您希望编写可互操作的 Web 服务,那么您就不应该使用 RPC/encoded 模式。


考虑其他的一些区别

JAX-WS 与 JAX-RPC 在操作映射方面的主要区别是前者引入了异步操作。任何具有双向消息流的 WSDL 操作,或者其中的客户端期待着接收响应的操作,都可以映射为异步的 Java 表示形式。有两种不同的机制,带回调的异步方式和异步轮询,它们需要两种不同的映射。后续的文章将描述这两种类型的操作是如何工作的。本文只是向您介绍一个示例。清单 12 包含一个异步回调操作,其中 javax.xml.ws.AsyncHandler 对象为回调对象。清单 13 包含了一个异步轮询操作映射。

清单 12. JAX-WS 异步回调
@WebMethod(action = "urn:samples.ibm.com/HelloWorld/hello")
@RequestWrapper(localName = "hello",
    targetNamespace = "urn:samples.ibm.com/HelloWorld", 
    className = "com.ibm.samples.helloworld.Hello")
@ResponseWrapper(localName = "helloResponse", 
    targetNamespace = "urn:samples.ibm.com/HelloWorld", 
    className = "com.ibm.samples.helloworld.HelloResponse")
public Future<?> helloAsync(
    @WebParam(name = "name", targetNamespace = "")
    String name,
    @WebParam(name = "asyncHandler", targetNamespace = "")
    AsyncHandler<String> asyncHandler);
清单 13. JAX-WS 异步轮询
@WebMethod(action = "urn:samples.ibm.com/HelloWorld/hello")
@RequestWrapper(localName = "hello", 
    targetNamespace = "urn:samples.ibm.com/HelloWorld", 
    className = "com.ibm.samples.helloworld.Hello")
@ResponseWrapper(localName = "helloResponse", 
    targetNamespace = "urn:samples.ibm.com/HelloWorld", 
    className = "com.ibm.samples.helloworld.HelloResponse")
public Response<String> helloAsync(
    @WebParam(name = "name", targetNamespace = "")
    String name);

在 JAX-RPC 中,没有用于 WSDL 操作的异步映射,所以这里没有办法进行比较。然而,重要的是,异步映射仅适用于客户端。对于服务端点,不存在类似的异步映射,只能用于客户端。

比较 IN/OUT 参数

JAX-RPC 和 JAX-WS 都支持称为 IN/OUT 参数的参数。在清单 14 中,您可以看到在清单 1 的 WSDL 中添加了一个 IN/OUT 参数。请注意,不管对于输入还是输出,都出现了名为“inout”的参数。在这个场景中,JAX-RPC 和 JAX-WS 将该参数映射为一个 Holder 参数,但是对于不同的映射,其效果是不同的。

清单 14. 带 IN/OUT 参数的 WSDL
<xsd:element name="hello">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="name" nillable="true" type="xsd:string" />
      <xsd:element name="inout" nillable="true" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

<xsd:element name="helloResponse">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="response" nillable="true" type="xsd:string" />
      <xsd:element name="inout" nillable="true" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

清单 15 提供了 Holder 参数的 JAX-RPC 映射,而清单 16 提供了 JAX-WS 映射。

清单 15. 带 IN/OUT 参数的 JAX-RPC SEI
public interface HelloWorld extends java.rmi.Remote {
    public java.lang.String hello(
        java.lang.String name, 
        javax.xml.rpc.holders.StringHolder inout) throws java.rmi.RemoteException;
}
清单 16. 带 IN/OUT 参数的 JAX-WS SEI
@WebService(name = "HelloWorld", targetNamespace = "urn:helloWorld/sample/ibm/com")
public interface HelloWorld {

    @WebMethod(action = "urn:helloWorld/sample/ibm/com/hello")
    @RequestWrapper(localName = "hello", 
        targetNamespace = "urn:helloWorld/sample/ibm/com", 
        className = "helloworld.sample.ibm.com.Hello")
    @ResponseWrapper(localName = "helloResponse", 
        targetNamespace = "urn:helloWorld/sample/ibm/com", 
        className = "helloworld.sample.ibm.com.HelloResponse")
    public void hello(
        @WebParam(name = "name", targetNamespace = "")
        String name,
        @WebParam(name = "inout", targetNamespace = "", mode = Mode.INOUT)
        Holder<String> inout,
        @WebParam(name = "response", targetNamespace = "", mode = Mode.OUT)
        Holder<String> response);

}

对于 JAX-RPC,规范中为各种已知的类型定义了一组类作为 Holder 类。这些类型包括 java.lang.String 和其他基本数据类型。对于用户定义的类型,JAX-RPC 需要生成能够处理用户定义类型的自定义的 Holder 类。另一方面,对于 JAX-WS,可以使用 Java 5 的泛型编程特性,提供适用于所有类型(包括用户定义类型)的单个类。

另一个有趣的内容是返回类型的区别。JAX-WS 并不像 JAX-RPC 那样保存返回类型,而是让方法的返回类型为 void,并使用 Holder 来获取返回值。根据 JAX-WS 中的规则,如果对于一个操作存在多个可以作为 OUT 参数的参数,那么返回类型必须为 void,并且将所有的 OUT 参数映射为 Holder 类型。


总结

上面的示例说明,尽管 JAX-RPC 和 JAX-WS 之间存在很多的区别,但是从 WSDL 到服务端点接口结构的映射是非常类似的。关键的区别包括:

  • 包名不同。
  • JAX-RPC 需要 java.rmi.Remote 和 java.rmi.RemoteException,而 JAX-WS 不需要。
  • 对 Holder 进行了不同的定义。

即使存在这些类似的地方,但是有一个主要的区别,使得 JAX-WS SEI 不同于 JAX-RPC。JSR-181 注释的使用,使得 JAX-WS SEI 可以以 Java 为中心或者以 WSDL 为中心来表示 Web 服务。可以包括大量的注释,以便将 Java 信息映射回 WSDL 构件。JAX-RPC SEI 无法通过任何形式来提供这类信息。还有一些特性是 JAX-WS 所特有的,即异步调用模型,以及它不需要提前生成 SEI。另一方面,JAX-RPC 也具备一些 JAX-WS 所没有的特性:它支持 RPC/encoded WSDL。

参考资料

学习

获得产品和技术

  • 使用 IBM 试用软件开发您的下一个项目,可下载或索取 DVD 光盘。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=253457
ArticleTitle=Web 服务提示与技巧: JAX-RPC 与 JAX-WS 的比较,第 3 部分
publish-date=09062007