IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  SOA and Web services | Information Management  >

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

动态调用接口

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

Russell Butek (butek@us.ibm.com), IT 专家, IBM 
Nicholas Gallardo (nlgallar@us.ibm.com), 软件工程师, IBM 

2007 年 9 月 06 日

本系列文章讨论 Java™ API for XML-based RPC (JAX-RPC) 1.1 和 Java API for XML Web Services (JAX-WS) 2.0,本文是其中的第 4 部分,将对动态调用模型进行比较。本文将给出每个模型的示例,以介绍其相似处和主要差异。

引言

JAX-RPC 1.1 和 JAX-WS 2.0 客户机动态模型都提供一组类似的抽象步骤来进行调用。

  1. 定义服务。
  2. 从此服务创建动态调用对象。
  3. 构建消息。
  4. 调用操作。

尽管这两个模型都采用相同的步骤,不过本文将说明两个模型间细节的差异。我们将使用前一篇文章中所用的 HelloWorld Web 服务描述语言(Web Services Description Language,WSDL)。具体如清单 1 中所示。


清单 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>





回页首


JAX-RPC 的动态调用接口

JAX-RPC 的动态调用接口(Dynamic Invocation Interface,DII)是 Call 对象 (javax.xml.rpc.Call)。清单 2 中给出了使用 Call 对象调用清单 1 的 HelloWorld 服务的完整客户机主类。还可以在清单 2 的介绍中看到对抽象步骤进行了说明。


清单 2. JAX-RPC 的 DII 客户机
                package com.ibm.samples.dii;

import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;

public class HelloWorldClient {

    public static void main(String[] args) {
        try {
            // Define the service.
            QName serviceName = new QName(
                    "urn:helloWorld/sample/ibm/com",
                    "HelloWorldService");
            ServiceFactory factory = ServiceFactory.newInstance();
            Service service = factory.createService(serviceName);

            // Create the dynamic invocation object from this service.
            Call call = service.createCall();
            call.setTargetEndpointAddress(
                    "http://localhost:9081/HelloWorldService/services/port");

            // Build the message.
            QName operationName = new QName(
                    "urn:helloWorld/sample/ibm/com",
                    "hello");
            call.setOperationName(operationName);
            call.addParameter(
                    "name",             // parameter name
                    XMLType.XSD_STRING, // parameter XML type QName
                    String.class,       // parameter Java type class
                    ParameterMode.IN);  // parameter mode
            call.setReturnType(XMLType.XSD_STRING);
            call.setProperty(
                    Call.OPERATION_STYLE_PROPERTY,
                    "wrapped");

            // Invoke the operation.
            Object[] actualArgs = {"Georgia"};
            String response = (String) call.invoke(actualArgs);
            System.out.println("response = " + response);
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
    }

下面让我们了解一下这些抽象步骤的细节:

  1. 定义服务。通过使用 WSDL 的服务完全限定名称构造 javax.xml.rpc.Service 对象。
  2. 从此服务创建动态调用对象。在 JAX-RPC 中,动态调用对象为 javax.xml.rpc.Call
  3. 构建消息。在此步骤中,将使用关于操作的信息填充 Call 对象。此处需要提出的一点是:我们将调用 call.setProperty(Call.OPERATION_STYLE_PROPERTY, "wrapped");wrapped 不是 JAX-RPC 为此属性定义的值。JAX-RPC 仅定义 rpcdocument。不过,rpc 实际上表示 RPC/Encoded,而 document 实际表示 Document/Literal, Non-Wrapped。自从 JAX-RPC 推出后,Document/Literal Wrapped 模式就成为了行业标准,因此其 Call 对象并不能很好地对其进行处理。可以完成此工作,但效果不太好。对此属性进行扩展,以包括 wrapped 值,这是 IBM 填补此规范空白的方法,但并非标准扩展。
  4. 调用操作。此示例中的输入是一个简单的字符串,因此将使用字符串填充参数,并将其传递给调用函数。响应也是字符串,在此示例中将直接对其进行显示。

JAX-WS 的动态 Dispatch 接口

JAX-WS 的 DII 是 Dispatch 对象 (javax.xml.ws.Dispatch)。清单 3 中给出了通过 Dispatch 对象调用清单 1 的 HelloWorld 服务的完整客户机主类。可以在清单 3 的介绍中看到对抽象步骤进行了说明。


清单 3. JAX-WS 的 DII 客户机
                import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

public class HelloWorldClient {

    public static void main(String[] args) {
        try {      
            // Define the service.
            QName svcQname = new QName(
                    "urn:helloWorld/sample/ibm/com", 
                    "HelloWorldService");
            QName portQName = new QName(
                    "urn:helloWorld/sample/ibm/com", 
                    "port");
            Service svc = Service.create(svcQname);
            svc.addPort(
                    portQName, 
                    SOAPBinding.SOAP11HTTP_BINDING,
                    "http://localhost:9080/JAXBSampleWebService/HelloWorldService");

            // Create the dynamic invocation object from this service.
            Dispatch<Source> dispatch = svc.createDispatch(
                    portQName, 
                    Source.class, 
                    Service.Mode.PAYLOAD);

            // Build the message.
            String content = 
                    "<ns2:hello xmlns:ns2=\"urn:helloWorld/sample/ibm/com\">" +
                      "<name>Georgie</name>" +
                    "</ns2:hello>";
            ByteArrayInputStream bais = new ByteArrayInputStream(content.getBytes());
            Source input = new StreamSource(bais);

            // Invoke the operation.
            Source output = dispatch.invoke(input);

            // Process the response.
            StreamResult result = new StreamResult(new ByteArrayOutputStream());
            Transformer trans = TransformerFactory.newInstance().newTransformer();
            trans.transform(response, result);
            ByteArrayOutputStream baos = (ByteArrayOutputStream) result.getOutputStream();

            // Write out the response content.
            String responseContent = new String(baos.toByteArray());
            System.out.println(responseContent);
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

接下来让我们看看此 JAX-WS 动态客户机中的这些抽象步骤的细节:

  1. 定义服务。与 JAX-RPC 模型中类似,将基于完全限定名称定义服务,但还要定义该服务的端口。
  2. 从此服务创建动态调用对象。在 JAX-WS 中,动态调用对象为 javax.xml.ws.Dispatch。这个类利用了 Java 5 中的新泛型功能支持多参数类型。
  3. 构建消息。在此处构建原始 SOAP 消息主题内容。
  4. 调用操作。您将 SOAP 主题的内容发送到服务,并从服务接收 SOAP 响应。




回页首


模型间的差异

从抽象步骤来看,JAX-RPC 和 JAX-WS 的动态调用模型非常相似。但事实并非如此。看看每个示例的细节,就会发现这些模型实际上有很大的区别。接下来让我们更为深入地分析一下其中的一些差异。

操作样式

相当清楚的是,两个模型之间的主要差异在于,JAX-RPC 采用远程过程调用(Remote Procedure Call,RPC)模型(即规范名称中的 RPC),而 JAX-WS 的动态客户机采用消息传递模型。对于 JAX-RPC,必须配置 Call 对象来显式地从 WSDL 调用特定操作。在 JAX-WS 中,Dispatch 对象不知道所调用的是哪个操作,仅仅负责发送 XML 数据而已。

参数样式

对于 JAX-RPC,所传递的参数类型保留规范定义的 XML 模式到 Java 的映射。对于 JAX-WS,可以采用两种不同的方式使用 Dispatch 对象:作为基于 XML 的 Dispatch(如本文中所示)或作为基于 JAXB 的 Dispatch。基于 XML 的 Dispatch 接受基于以下对象类型的参数:javax.xml.transform.Sourcejavax.xml.soap.SOAPMessagejavax.activation.DataSource。基于 JAXB 的 Dispatch 要求用户配置可用于封送和取消封送参数实例(JAXB Java Bean)的 javax.xml.bind.JAXBContext

参数样式之间的另一个主要差异在于所传递的内容。对于 JAX-RPC,参数始终作为请求的参数数据使用。而对于 JAX-WS,有两种不同的参数模式:有效负载 (PAYLOAD) 和消息 (MESSAGE) 模式。PAYLOAD 模式表示参数仅仅代表 SOAP 主体的内容,而 MESSAGE 模式表示参数代表整个消息,包括 SOAP 信封。

调用模式

如上所述,JAX-RPC 和 JAX-WS 都通过 invoke() 方法提供了同步双向调用。这两个模型都提供了用于通过 invokeOneWay 方法调用单向 Web 服务操作的方法。两个模型之间的主要差异在于,JAX-WS 还提供异步调用模型。其中包含异步回调模型和异步轮询模型。JAX-RPC 并不提供异步调用选项。

服务器端动态编程模型

此处将不会详细讨论的另一个主要差异是,JAX-WS 添加了 JAX-RPC 永远不会有的内容——动态服务器端编程模型。与客户端模型类似,也可以将其配置为使用 PAYLOAD 或 MESSAGE 模式。





回页首


总结

共享本教程

digg 请 Digg 这个故事
del.icio.u 发布到 del.icio.u
Slashdot Slashdot 一下!

JAX-RPC 和 JAX-WS 都提供了动态客户机模型。简单而言,可以将二者视为对等。但仔细分析其细节,可以发现 JAX-RPC 的动态模型是 RPC 模型,而 JAX-WS 的动态模型为消息传递模型。JAX-WS API 可提供更大的灵活性,是 Web 服务编程模型发展的下一步目标。JAX-WS 还提供了异步支持和动态服务支持,而 JAX-RPC 并未对此进行定义。



参考资料

学习

获得产品和技术
  • 使用 IBM 试用软件开发您的下一个项目,可下载或索取 DVD 光盘。


讨论


作者简介

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


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




对本文的评价










回页首


IBM、IBM 徽标和 WebSphere 是 IBM 在美国和/或其他国家/地区的注册商标。 Java 和所有基于 Java 的商标都是 Sun Microsystems, Inc. 在美国和/或其他国家/地区的商标。 其他公司、产品或服务的名称可能是其他公司的商标或服务标志。

IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款