您的位置:首页 > 其它

松散类型 Web 服务与强类型 Web 服务

2006-04-25 01:00 246 查看
您是否知道松散类型 Web 服务和强类型 Web 服务编程方法的区别?IBM 高级技术人员 Andre Tost 将对这些区别加以说明,并将解释为什么大多数情况下需要强类型服务。
引言

Web 服务越来越多地用于企业应用程序集成(Enterprise Application Integration,EAI),在 EAI 中,服务使用者和服务提供者使用企业服务总线(Enterprise Service Bus,ESB)进行通信。企业应用程序的集成由来已久了,通常是通过创建应用程序适配器完成的,应用程序适配器会将消息转换为内部专用格式和协议。

从表面上看,使用 Web 服务对应用程序进行集成似乎是和以前的集成方案一样(或者,至少十分相似)的概念。但与过去相比,Web 服务能使标准化程度更高,且能提供更好的工具,平台与编程语言间的互操作性也更好。

描述系统的相关服务接口仍然是一个挑战。开发人员通常使用两种方法之一应对这项挑战。一方面,Web 服务描述语言(Web Services Description Language,WSDL)定义可以利用消息的 XML 架构描述,允许准确描述交换的消息。另一方面,很多服务均使用通用接口构建:一个字符串传入,另一个字符串传出,或者交换纯二进制对象。对消息的正确解释的工作留给了使用者和提供者。尽管在出现更改时,认为这种方法更为灵活(因为消息的具体细节在服务实现中处理),但同时也更容易出错,而且要求进行更多的手动编程工作。

在本文中,我将比较这两种方式,并会就每种方式的应用场合进行举例说明。








回页首
松散类型 Web 服务

首先,让我们定义本文范围内的松散类型这一术语。松散类型意味着服务的接口定义(使用 WSDL)不包含强定义服务所使用的任何消息格式的架构。也就是说,应用程序可以不完全单独遵从来自服务接口定义的消息实例。

松散类型描述的一个例子就是将消息的实际内容编码为单个字符串的 Web 服务。此服务接口描述一个 xsd:string 类型的输入参数和一个输出参数。其 WSDL
<types>
部分可能与下面的清单 1 类似。

清单 1. WSDL <types>

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitionstargetNamespace="http://mycomp.com"xmlns:impl=
"http://mycomp.com"xmlns:intf="http://mycomp.com"xmlns:wsdl=
"http://schemas.xmlsoap.org/wsdl/"xmlns:wsdlsoap=
"http://schemas.xmlsoap.org/wsdl/soap/"xmlns:wsi=
"http://ws-i.org/profiles/basic/1.1/xsd"xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schematargetNamespace="http://mycomp.com"xmlns=
"http://www.w3.org/2001/XMLSchema"xmlns:impl=
"http://mycomp.com"xmlns:intf=
"http://mycomp.com"xmlns:wsdl=
"http://schemas.xmlsoap.org/wsdl/"xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<element name="executeResponse">
<complexType>
<sequence>
<element name="executeReturn" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</element>
<element name="execute">
<complexType>
<sequence>
<element name="message" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>

</wsdl:definitions>

请注意,由于这是“包装的文档/文本”Web 服务,因此包装元素称为“execute”。(此处并显示所有 WSDL,但剩余部分肯定包含文档/文本绑定。)单个输入和输出参数定义以粗体突出显示。

对于此定义,您并不能准确地知道请求和响应消息的内容的格式。它可能为字符分隔的字符串、固定长度的字符串,或者,甚至可以是 XML。无论何种情况,服务使用者和服务提供者需要就双方都能理解的共同格式达成一致,而且双方都需要开发代码以正确地构建和解释该格式。由于消息内容的具体定义并没有包括在 WSDL 定义中,因此这里没有可提供帮助的工具。

有利的一面是,如果消息结构发生更改,您无须更改新 WSDL 定义。只要确保所有的参与方均知道这一更改,并且能处理更改后的格式就行了。不过,此方法的缺点通常比优点更为突出,特别在将 XML 作为内容发送时更是如此。

在字符串中发送 XML

让我们更进一步了解在此字符串中发送 XML 数据时的情况。当现有应用程序包含 XML 格式的信息,并且要将此数据原封不动地通过 Web 服务接口发送时,就可能出现这种情况。(由于 XML 可以表示为字符串,所以将数据表示为字符串参数。)例如,假设需要使用以下格式的 XML 文档作为客户机请求的响应发送,如下面的清单 2 所示。

清单 2. 在字符串中发送 XML

Note:  This is an example only.
<customer>
<name>Joe Smith</name>
<address>
<street>4308 Sunshine Blvd</street>
<city>Rochester</city>
<state>MN</state>
<zip>55901</zip>
</address>
</customer>

清单 3 演示了当使用清单 2 中的服务接口定义时,简单对象访问协议 (SOAP) 响应消息的格式。

清单 3. SOAP 响应消息

<soapenv:Envelope xmlns:soapenv
="http://schemas.xmlsoap.org/soap/envelope/"xmlns:soapenc=
"http://schemas.xmlsoap.org/soap/encoding/"xmlns:xsd=
"http://www.w3.org/2001/XMLSchema"xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<p416:executeResponse xmlns:p416="http://mycomp.com">
<executeReturn><customer>   <name>Joe Smith</name>
<address>   <street>4308 Sunshine Blvd</street>
<city>Rochester</city>   <state>MN</state>
<zip>55901</zip>   </address></customer></executeReturn>
</p416:executeResponse>
</soapenv:Body>
</soapenv:Envelope>

这看起来并不像 XML,对吧?这是由于对 SOAP 引擎而言,它是一个字符串。如果将 XML 传递到此字符串中,将对其进行编码,以使其不干涉 SOAP 引擎的 XML 处理。不过,在将其送回客户机之前,会再将已编码的字符串解码。此过程对于实际的服务客户机和提供者实现都是透明的,而这无疑是非常有利的。但另一方面,它增加了消息的大小,使得调试 SOAO 消息传递更难了,并增加了每条消息的处理时间。在本文的稍后部分,我将给出另一个示例,该示例将提供同样的功能,但避免了其中的这些问题。在这里我要指出的是,使用字符串参数通过 Web 服务接口发送 XML 通常被认为是糟糕的设计,应该加以避免。

有关在字符串中发送 XML 的示例,请参阅本文中提供的 EAR 文件GenericString.ear

使用 <xsd:any>

指示 WSDL 文件中不存在消息格式的严格定义的另一种方式就是使用
<xsd:any/>
元素。在架构中出现此元素将仅指示在运行时此位置可以出现任何类型的 XML。区别在于,实际的 XML 将进入消息中,而不会转换为编码的字符串。同时,消息的处理仍然必须手动编码,因为 WSDL 不包含任何关于此消息的提示。

因此 Java™ APIs for XML-Based Remote Procedure Call (JAX-RPC) 规范描述了将
<xsd:any/>
映射到 javax.xml.soap.SOAPElement。此接口属于标准 SOAP with Attachments API for Java (SAAJ API),定义 SOAP 消息各个元素的 Java 接口。可以采用分析普通 Document Object Model (DOM) 的方式对其进行分析。我将在后面给出这一点的示例。

以下是前面所使用过的 WSDL 定义;不过,我使用
<xsd:any/>
代替了泛型字符串,如下面的清单 4 所示。

清单 4. <xsd:any/>

<wsdl:definitions targetNamespace="http://mycomp.com"xmlns:impl=
"http://mycomp.com"xmlns:intf="http://mycomp.com"xmlns:wsdl=
"http://schemas.xmlsoap.org/wsdl/"xmlns:wsdlsoap=
"http://schemas.xmlsoap.org/wsdl/soap/"xmlns:wsi=
"http://ws-i.org/profiles/basic/1.1/xsd"xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schema targetNamespace="http://mycomp.com" xmlns=
"http://www.w3.org/2001/XMLSchema" xmlns:impl=
"http://mycomp.com"xmlns:intf="http://mycomp.com" xmlns:wsdl=
"http://schemas.xmlsoap.org/wsdl/" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<element name="executeResponse">
<complexType>
<sequence>
<xsd:any/>
</sequence>
</complexType>
</element>
<element name="execute">
<complexType>
<sequence>
<xsd:any/>
</sequence>
</complexType>
</element>
</schema>
<wsdl:types>
<wsdl:definitions/>

使用
<xsd:any/>
比使用泛型字符串更可取,因为这样就不需要对消息进行编码。不过,它只能和 XML 消息一起使用,而且仍然要求代码出现在服务提供者和使用者端,以分析和构建消息。清单 5 演示了将我在清单 4 中所使用的相同的 Customer XML 文档发送回时的服务提供者的代码:

清单 5. 服务提供者代码

package com.mycomp;

import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;

import com.ibm.websphere.webservices.soap.IBMSOAPFactory;

public class GenericAnySoapBindingImpl implements com.mycomp.GenericAny{

public String customer =
"<customer>"  +
"<name>Joe Smith</name>"  +
"<address>"  +
"<street>4308 Sunshine Blvd</street>"  +
"<city>Rochester</city>"  +
"<state>MN</state>"  +
"<zip>55901</zip>"  +
"</address>"  +
"</customer>";

public javax.xml.soap.SOAPElement execute(javax.xml.soap.SOAPElement any)
throws java.rmi.RemoteException {
SOAPElement result = null;
try {
SOAPFactory factory = SOAPFactory.newInstance();
result =
((IBMSOAPFactory)factory).createElementFromXMLString(customer);
} catch (SOAPException x) {}
return result;
}

}

清单 6 显示了以上请求的 SOAP 响应消息的内容。

清单 6. SOAP 响应消息

Note:  This is an example only.

<soapenv:Envelope xmlns:soapenv=
"http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema" xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<p416:executeResponse xmlns:p416="http://mycomp.com">
<customer>
<name>Joe Smith</name>
<address>
<street>4308 Sunshine Blvd</street>
<city>Rochester</city>
<state>MN</state>
<zip>55901</zip>
</address>
</customer>
</p416:executeResponse>
</soapenv:Body>
</soapenv:Envelope>

这比您在清单 5 中所看到的消息更具可读性,因为没有对其进行编码。不过,我必须手动构建响应消息。另外,我没有指出客户机响应的消息的架构。

本文中的 GenericAny.ear 部分提供了一个完整的示例,其中演示了
<xsd:any/>
元素的用法。

松散类型服务与文档/文本样式

松散类型 Web 服务的使用经常与在 WSDL 中定义的服务样式相关。可以将服务的调用样式定义为“文档”或“RPC”,编码方式可为“SOAP”或“文本”。这两项的最常见组合称为“文档/文本”。大部分 Web 服务引擎在缺省情况下都使用“包装的文档/文本”样式。有关这些不同样式的相关文档,请参阅 developerWorks 文章“Which style of WDSL should I use?”。

不过,所使用的样式与是否创建松散类型或泛型的接口没有任何关系。“包装的文档/文本”样式已定义,它将以调用远程过程结束。此远程过程具有一个或多个泛型参数,或强类型参数。同样,您也可以定义一个泛型接口(如上面仅接受一个字符串的接口),然后在 WSDL 绑定中将其声明为“rpc/encoded”。

换句话说,调用样式和接口的强类型这两项设计决策彼此的影响很小(如果有)。

松散类型 Web 服务评估

如上所述,松散类型 Web 服务可以在前面的接口定义中没有声明数据的结构情况下发送数据。很多情况下,这都很有用。例如,假设您具有一组粗粒度的服务,这些服务将可能很大的 XML 文档作为输入。这些文档可能具有不同的结构,具体取决于其使用的上下文。而且,这个结构可能在服务的生存期中经常更改。可以采用某种方式实现一个 Web 服务,使其可以处理所有这些不同类型的消息(很可能对消息进行分析,然后将其路由到其最终的目的地)。可以对消息格式进行更改,使它们具有向后兼容性,这样就无需更新现有的代码。

另一个例子是使用中介。中介具有 Web 服务接口,并接收消息。但很多时候,中介将消息路由到其最终的目的地之前仅对其进行某种常规处理。例如,提供消息记录功能的中介不需要强类型接口,因为它只记录其所接收到的所有消息的内容。

最后,可能存在不能在 XML 架构中描述的消息格式,或选择的 Web 服务引擎不能处理所得到的架构。例如,JAX-RPC 和 Java Architecture for XML Binding (JAXB) 规范就没有定义完整的 XML 架构元素的 Java 映射集。由于存在不能映射的行业标准架构,因此无论如何都必须手动对遵循这些标准的消息进行编码。大部分 JAX-RPC 一致性工具均采用与处理
<xsd:any/>
相似的方式处理这些架构元素,即将其映射到 javax.xml.soap.SOAPElement。

另一方面,您已在上面的示例中已看到,在 SOAP 信封中发送此类任意消息经常需要 SOAP 引擎进行额外的处理。消息一旦经过编码,其线格式可读性可能并不好。此外,您必须手动编写代码以处理消息的有效负载。由于 WSDL 中并没有消息的确切定义,因此 Web 服务工具不能生成此代码,而这将导致此类解决方案更容易出错。无法进行消息验证。如果消息格式更改,那么与确保所有使用者和提供程者正确处理新格式相比,更新服务接口并重新生成绑定代码会更容易。

了解了松散类型方法的种种明显缺点之后,让我们了解一种更好的选择。








回页首
强类型 Web 服务

就像上面讨论松散类型服务时一样,也需要对“强类型”这一术语加以定义。该术语指在 XML 架构中包含了完整的输入与输出消息定义的服务,此 XML 架构或者包含在 WSDL 定义中,或者由该 WSDL 定义引用。

服务使用者和服务提供者必须交换的唯一信息就是 WSDL 定义,因为该定义中包含着构建它们所需的所有信息。例如,以上面将 Customer 文档作为 Web 服务的结果返回的示例为例,强类型 WSDL 定义可能与下面的清单 7 类似。

清单 7. 强类型 WSDL 定义

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://mycomp.com" xmlns:impl=
"http://mycomp.com" xmlns:intf="http://mycomp.com" xmlns:wsdl=
"http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap=
"http://schemas.xmlsoap.org/wsdl/soap/"xmlns:wsi=
"http://ws-i.org/profiles/basic/1.1/xsd" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schema targetNamespace="http://mycomp.com" xmlns=
"http://www.w3.org/2001/XMLSchema" xmlns:impl=
"http://mycomp.com" xmlns:intf="http://mycomp.com" xmlns:wsdl=
"http://schemas.xmlsoap.org/wsdl/" xmlns:xsd=
"http://www.w3.org/2001/XMLSchema">
<element name="getCustomerResponse">
<complexType>
<sequence>
<element name="getCustomerReturn" nillable="true" type="impl:Customer"/>
</sequence>
</complexType>
</element>
<element name="getCustomer">
<complexType>
<sequence>
<element name="criteria" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</element>
<complexType name="Customer">
<sequence>
<element name="address" nillable="true" type="impl:Address"/>
<element name="name" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="Address">
<sequence>
<element name="city" nillable="true" type="xsd:string"/>
<element name="state" nillable="true" type="xsd:string"/>
<element name="street" nillable="true" type="xsd:string"/>
<element name="zip" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</schema>
</wsdl:types>
</wsdl:definitions>

现在将不会简单地定义一个字符串或
<xsd:any/>
参数作为返回类型,而要使用一个名为 Customer 的已完整定义的复杂类型。可以将这用于生成 Java 代码,以将任何消息映射到具有 getter 和 setter 的常规 Java 对象中。您无需编写代码来手动分析 XML。

清单 8 显示了此类服务的 Java 实现的格式。

清单 8. 已定义的复杂类型

package com.mycomp;

public class TypedCustomerRetrieval {
public Customer getCustomer(String criteria) {
Customer customer = new Customer();
Address address = new Address();
address.setCity("Rochester");
address.setState("MN");
address.setStreet("4308 Sunshine Blvd");
address.setZip("55901");

customer.setName("Joe Smith");
customer.setAddress(address);
return customer;
}

}

JAX-RPC 工具将生成这里使用的 Customer 和 Address 类。与上面的清单 6 一样,通过网上传递的数据是纯 XML。事实上,这些数据与
<xsd:any/>
示例中的数据完全一样。另外还有一个好处,由于存在消息的完整架构,因此可以根据此架构进行验证(尽管大部分引擎由于性能的原因并不这样做)。

本文提供了完整的 TypedService.ear 文件。此文件以及本文的所有其他 ear 文件都还包含测试客户机。

接口与实现

我前面曾提到,某些架构元素不能映射到 Java 中。另外,服务实现者有时候也可能希望开发自己的方式映射传入的 SOAP 消息。另一个例外就是作为 Web 服务一部分的文档可能具有很多元素。服务的开发人员可能不希望将其映射到具有数百个参数的服务接口。

这些情况不要求将 Web 服务设计为松散类型服务,而需要就接口与实现进行讨论。服务可以在其接口定义中采用强类型方式,而在其实现中采用泛型方式 。JAX-RPC 规范要求一致性引擎提供跳过常规 XML 到 Java 类型映射的方法,而使用备用机制。例如,WebSphere® Application Server (Application Server) 允许将任何请求和/或响应消息映射为 javax.xml.soap.SOAPElement 类型的实例(与
<xsd:any/>
的映射方式相似,请参阅上面的清单 4)。例如,上面所列出的 WSDL 摘要中包含完整的类型定义,可以将其映射为以下 Java 服务端点接口,如下面的清单 9 所示。

清单 9. Java 服务端点接口

注意:这仅是一个示例。

public interface TypedCustomerRetrieval extends java.rmi.Remote {
public javax.xml.soap.SOAPElement getCustomer
(javax.xml.soap.SOAPElement parameters)
throws java.rmi.RemoteException;
}

在此接口中没有 Customer 或 Address 的概念。它完全留给开发人员处理传入和传出的 XML。(请参阅参考资料部分,参考条目中给出了更为详细描述这方面的文章。)

强类型 Web 服务评估

强类型 Web 服务为服务的使用者和提供者提供了服务的数据结构的完整定义。这种正式约定使 SOAP 和 XML 的使用对客户端代码和服务器端代码透明,工具可以方便地从这种正式约定生成代码。如上面所示,在不能或不应使用生成的代码的情况下,可以采用松散类型方式实现强类型接口。顺便说一下,服务提供者和服务使用者可以采用不同的方式进行实现。服务实现者可能喜欢采用手动方式对消息进行映射和分析。不过,通过为使用者提供完整的架构,该使用者可以选择借助工具生成适当的映射代码。这两者之间不需要任何进一步的文档。

在这两种情况下,由于不需要进行编码,所以通过网络传输的消息通常会更小。








回页首
总结

在本文中,我演示了 Web 服务可以如何具有不同的类型级别。松散类型服务使用泛型参数定义,其中不以架构的形式包含消息内容的说明。它们可以包含二进制、字符串或 XML 格式(经过编码的)的信息,但需由服务的使用者和提供者就使用的格式达成一致,并相应地进行处理。当消息格式发生更改,消息格式会随服务内容不同而变化,以及处理普通工具不能处理的高级复杂数据结构时,松散类型服务更加灵活。另外,它们还可以对消息解释进行手动优化。

强类型 Web 服务在 WSDL 中包含完整的架构定义。通常这将提高自动化程度、代码生成质量、工具支持水平和改善标准化中间件的使用。这样还能产生更为稳定的代码,使开发人员不必创建基础结构级别的代码。而且,即使采用泛型方式实现的服务也可以提供强类型接口定义。

在我所遇到的大多数实际场景中,强类型服务接口更为适用。正如我在前面指出的,最好避免使用泛型字符串类型的接口。

不过,并没有万能的东西。常常需要由设计解决方案的架构师确定描述接口的最佳方法,具体取决于解决方案的具体要求、运行环境,甚至还要考虑开发团队的首选的样式。








回页首
下载

描述名字大小下载方法
EAR 文件ws-loosevstrong.zip120 KBFTP


关于下载方法的信息

Get Adobe® Reader®







回页首
参考资料

学习

您可以参阅本文在 developerWorks 全球站点上的 英文原文

"Web 服务编程技巧和诀窍: 将 <xsd:any/>元素用于自定义序列化"(developerWorks,2004 年 1 月)。这篇文章演示了如何在 WSDL 中使用 <xsd:any/>。

"Web 服务编程技巧与窍门: 用 SAAJ 和 JAX-RPC 构建 SOAP 响应信封"(developerWorks,2004 年 1 月)。上面文章的续篇,演示了如何使用 SAAJ 构建 SOAP 响应信封。

"我应该采用哪一种 WSDL 样式?"(developerWorks,2005 年 5 月)。一篇关于调用样式的好文章。

"Developer's introduction to JAX-RPC, Part 1: Learn the ins and outs of the JAX-RPC type-mapping system"(developerWorks,2002 年 11 月)。JAX-RPC 规范简介。

IBM developerWorks 团队会在全球举办数百场可免费参加的 technical briefings

Standards roadmap——了解标准和规范对于 SOA 和 Web 服务开发工作的影响和重要性。

SOA and Web services——有数百篇关于如何开发 Web 服务应用程序的文章以及入门级、中级和高级教程,将让您大开眼界。

获得产品和技术

了解 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 中的应用程序开发工具和中间件产品。您可以免费下载这些产品的评估版,也可以选择 Linux™ 或 Windows® 版本的 developerWorks 的免费 Software Evaluation Kit

讨论

developerWorks 博客:加入 developerWorks 社区。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐