级别: 初级 Andre Tost, 高级认证 IT 专家, IBM Software Group Tony Cowan (ttcowan@us.ibm.com), 高级认证 IT 专家, IBM Software Group
2004 年 6 月 01 日 本文介绍了几种技术,您可以使用这些技术构建可互操作 Web 服务来提取和返回对象集合。本文是涵盖这个主题的一系列文章的第一篇。后续的文章将会讨论更详细的情况。
对于应用程序来说,操纵对象集合、将它们作为参数传入函数并在处理完成时返回它们是非常常见的。在大部分编程语言中,都有许多方法来表示这些集合。它们可以表示为有序的向量、无序的组、链表、树、图或编程语言支持的其他形式。Java 语言在 java.util 包中提供了一个完整的集合类型库。但问题是,您应该如何通过 Web 服务调用来传递集合?
我可以使用我喜欢的 Java 集合类型吗?
为了阐述使用标准 Java 集合类带来的一些问题,让我们假定您想要一个带有
getCustomers 操作的顾客服务。您可以创建一个 JavaBean,让它根据一些搜索标准返回 Customer 对象的集合。在对 Java 语言提供的集合类作了一番考察之后,您决定使用
java.util.LinkedList 类。您的
CustomerService 类可能看起来像
清单 1中的代码样本:
清单 1. 使用 LinkedList 的 CustomerService 类
import java.util.LinkedList;
public class TheCustomerService {
public LinkedList getCustomers(String queryString) {
Customer customer1, customer2;
/* ... retrieve customers for query ... */
LinkedList list = new LinkedList();
/* iterate over the result set assigning to the list */
list.add(customer1);
list.add(customer2);
return list;
}
}
|
下一步是将这个类作为 Web 服务公开,因此您选择遵循 JAX-RPC 的工具来生成所需的构件,比如描述服务接口的
WSDL 文件。现在麻烦就来了。JAX-RPC 规范没有为
LinkedList 类定义映射。相反,它提到每个实现都可以决定是否以及如何映射这个类、还有这个类的实例是如何序列化到 XML 和从 XML 序列化来。
例如,下面的
清单 2显示了 WebSphere Application Server 工具生成什么作为清单 1 中列出的
类的 XML 模式:
清单 2. 为 CustomerService 类生成的 XML 模式
<schema elementFormDefault="qualified"
targetNamespace="http://pack"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://pack" xmlns:intf="http://pack"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<element name="getCustomers">
<complexType>
<sequence>
<element name="queryString"
nillable="true"
type="xsd:string"/>
</sequence>
</complexType>
</element>
<complexType name="ArrayOfXSDAnyType">
<sequence>
<element maxOccurs="unbounded"
minOccurs="0"
name="item"
nillable="true"
type="xsd:anyType"/>
</sequence>
</complexType>
<element name="getCustomersResponse">
<complexType>
<sequence>
<element name="getCustomersReturn"
nillable="true"
type="impl:ArrayOfXSDAnyType"/>
</sequence>
</complexType>
</element>
</schema>
|
正如您可以看到的,该工具为一些称为
ArrayOfXSDAnyType 的类型生成了复杂类型的定义,而它又是类型
<xsd:anyType> 的一个未绑定序列。它为什么生成
xsd:linkedList 类型的元素来包含您的 Customers 对象?这是因为没有
xsd:linkedList 。也没有
xsd:hashMap 、
xsd:treeSet 、
xsd:vector 和
xsd:stack 。所有这些虚构的类型除了可以存储有序的对象列表外,还具有一些与它们相关的隐含功能。每种语言、工具或环境都可以自由地提供任意的集合构造来完成下列工作:
- 实现隐含功能的变体
- 省去这种类型的集合
- 定义没有包括在 Java 集合中的其他类型
那么,您如何以互操作的方式交换对象集合呢?答案是使用数组。
解决方案?普通数组……
虽然一些可能确实能够在您的特定环境(比如 Java 到 Java 的环境)中正常工作,但是在其他的环境中却不尽然。因此,我们建议不要在服务接口上使用这些类。在 Web 服务之间转换对象集合的唯一方法是将其作为数组。WS-I 基本概要(WS-I Basic Profile)描述了如何将数组(它仍然是特定于语言的构造)映射到 XML、以及如何在 XML 模式中对此进行描述,以使得它的处理在多个环境中可互操作。
清单 3 显示了经过修改的 CustomerService JavaBean 看起来像什么:
清单 3. 使用简单数组的 CustomerService 类
public class TheCustomerService {
public Customer[] getCustomers(String queryString) {
Customer customer1, customer2;
/* ... retrieve customers for query ... */
/* create an array large enough to hold the result set */
Customer[] customers = new Customer[2];
/* iterate over the result set assigning to the array */
customers[0] = customer1;
customers[1] = customer2;
return customers;
}
}
|
正如您可以看到的,我们使用
Customer类型的数组替换了 LinkedList。
清单 4 显示了 WSDL 定义中的结果 XML 模式:
清单 4. 经过修改的 CustomerService 类的 XML 模式
<schema elementFormDefault="qualified"
targetNamespace=http://pack
xmlns=http://www.w3.org/2001/XMLSchema
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://pack" xmlns:intf="http://pack"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<element name="getCustomers">
<complexType>
<sequence>
<element name="queryString"
nillable="true"
type="xsd:string"/>
</sequence>
</complexType>
</element>
<complexType name="Customer">
<sequence>
<element name="customerID"
nillable="true"
type="xsd:string"/>
</sequence>
</complexType>
<element name="getCustomersResponse">
<complexType>
<sequence>
<element maxOccurs="unbounded"
name="getCustomersReturn"
type="impl:Customer"/>
</sequence>
</complexType>
</element>
</schema>
|
这样得到的模式定义和为这个服务生成的 SOAP 消息都是语言中立的,并且都遵循 WS-I 基本概要(WS-I Basic Profile)。
创建包装类接口
到目前为止,一切都很好。但是如果您有一个现有的类,它使用方法签名来公开特定于语言的 Java 集合类,那么您还想通过 Web 服务公开这个类吗?
处理这种情况的一个相对简单的方法是以您的服务实现为基础创建包装器类,将该集合接口转换成一个数组接口。例如,让我们考虑上面列出的基于原始 LinkedList 的 CustomerService 实现。我们创建一个类来调用基于原始集合的实现,但是它返回 Java 数组而不是返回集合。该包装器来可能看起来像
清单 5中的某一个:
清单 5. 包装器类
public class TheCustomerServiceWrapper {
protected TheCustomerServiceLinkedList innerService =
new TheCustomerServiceLinkedList();
public Customer[] getCustomers(String queryString) {
return (Customer[]) innerService.getCustomers(queryString
).toArray(new Customer[0]);
}
}
|
现在,我们可以基于这个包装器类来创建适当的 Web 服务构件了,它在内部使用现有的基于集合的实现。
总结
Java 编程语言为不同类型的集合对象提供了各种集合类。然而,这些类中没有一个是语言中立的,并且将它们的实例序列化到 XML 中非常困难,有时甚至是不可能的。我们推荐的用于公开对象集合的唯一方法就是使用数组。WS-I 基本概要(WS-I Basic Profile)也描述和推荐了这种方法。
不管实现是否与 Web 服务相关,在您的类的公共方法的签名中使用 Java 的集合类通常不是一个好的选择,因为这样做削弱了调用者和提供者之间的联系。这些类总是将它们收集的对象作为一般引用存储到 java.lang.Object 中。这避免了由编译器进行静态的检查,并且引入了新的运行时错误类。在实现已经存在的情况下,您还可以通过包装器类使用数组来将其作为可互操作的 Web 服务公开。
请注意,这种实践与我们推荐的良好实践很好地保持了一致,将业务逻辑编码在 Java 类中,并用会话 EJB 对其进行打包,以便作为 Web 服务公开。
下载 | 名字 | 大小 | 下载方法 |
|---|
| ws-tip-codingcode.ear | 36.0 KB | HTTP |
参考资料
作者简介  | |  | Andre Tost 是一位解决方案架构设计师,在 WebSphere Business Development 组工作,在那里,他帮助 IBM 的策略联盟合作伙伴使用 WebSphere 集成他们的应用程序。他的主要研究方向是在整个 WebSphere 产品家族中使用 Web 服务技术。在承担现在的工作之前,他在 IBM 软件开发中作为开发人员及架构师已经有 10 年了,最近主要是研究 WebSphere Business Components 产品。他来自德国,现在生活和工作在美国明尼苏达州罗彻斯特市。在他的空闲时间,他喜欢跟家人在一起玩或者看足球。您可以通过
atost@us.ibm.com与 Andre 联系。
|
 | |  | Tony Cowan 是 IBM Software Services for WebSphere (ISSW) 组中的一名高级顾问。他曾经在分布式系统开发中担任了 11 年的顾问,并且领导 IBM 组与财富杂志 1000 强的公司有许多项目合作。Tony 当前专注于给 IBM 顾问和客户讲授 Web 服务和 Web 服务安全方面的课程。Tony 频频在各种技术会议上发言,在 IBM,他的主要目标之一就是将实际的客户要求带给 IBM 开发组,以帮助使 IBM 产品与现实世界的需要保持一致。您可以通过
ttcowan@us.ibm.com与 Tony 联系。
|
对本文的评价
|