WEB开发网
开发学院软件开发Java 设计移动 Web 服务 在设计移动 Web 服务时需要考虑... 阅读

设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次

 2007-12-23 12:30:50 来源:WEB开发网   
核心提示:从何时选择移动 Web 服务到总体设计指导原则再到用于移动 Web 服务的值类型,本文提出了在设计用于移动设备的 Web 服务时需要考虑的许多设计事项,设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次,文中还介绍了许多设计移动 Web 服务方面的最佳实践,从本文中,她对无线技术和 Web 服务非
从何时选择移动 Web 服务到总体设计指导原则再到用于移动 Web 服务的值类型,本文提出了在设计用于移动设备的 Web 服务时需要考虑的许多设计事项。文中还介绍了许多设计移动 Web 服务方面的最佳实践。从本文中,您可以了解如何决定何时使用 Web 服务、在设计 Web 服务时需要考虑什么事项,以及在规划移动 Web 服务时必须谨记哪些问题。

Web 服务是一种集成技术。在集成异构系统时 Web 服务的价值可以得到最好的证明,因为其支持许多类型的编程语言、运行时环境和网络。当需要从不兼容的环境连接应用程序时,Web 服务就有了用武之地。通过 Web 服务,您可以将业务应用程序从 java™ 2 Platform EnterPRise Edition (J2EE) 连接到 .NET。您还可以使用某个运行在 linux™ 中的应用程序将一个应用程序集成在 Windows™ 操作环境中。在本文中,我提供了一些针对移动 Web 服务的重要设计考虑事项,并且向您介绍了一些与之有关的最佳实践。

首先,我将讨论在开始之前 需要考虑哪些事项。

开始之前

在开始设计整个系统的体系结构之前,您必须做出如下决定——何时使用移动 Web 服务以及何时不使用移动 Web 服务。

对于移动设备,Web 服务是利用工作站的强大计算功能的一种最佳方式。Java Specification Request 172 (JSR-172) 定义了用于 Java 2 Platform Micro Edition (J2ME) 平台的 Web 服务 API。由于移动服务主要从客户端的角度进行编程并且是服务使用者,因此本文只需要介绍一部分远程服务调用 API (JAX-RPC) 和 JAXP (Java API for xml Parsing)。

设计移动 Web 服务的主要目的在于使嵌入式设备能够使用由服务器提供的服务,换句话说,移动 Web 服务是从 Web 服务使用者的角度进行设计的,目的在于支持轻量级设备共享服务器的计算功能和数据库

移动 Web 服务无缝地集成了运行在不同平台上的两种不同的应用程序,并且提供了它们之间的互操作性。通常,在考虑移动设备的参与时,有三种类型的集成技术可以运用:

  • 套接字通信
  • Web 服务
  • 消息传递技术(如 WebSphere® MQ Everyplace)

与套接字通信和消息传递技术相比,Web 服务有一些突出的优势。Web 服务使用可扩展标记语言 (XML) 来传输消息(包括结构良好的数据信息),使用简单对象访问协议 (SOAP) 来传输对象。如果您使用的是套接字通信,则必须完全负责定义要传输的数据结构。而且,如果客户端和服务器是用不同的编程语言编写的(例如 C++ 和 Java 编程语言),则您的工作量将大大增加——您必须负责数据传输和 C++ 和 Java 编程中的编码细节。

消息传递软件可能是一种解决方案,但如果您所关注的是性能,并且不担心事务和安全级别,则消息传递软件真的不是一个非常好的选择。如果使用消息传递软件,您将花大量的时间和精力解决安全问题,并且您的客户很有可能站在您的门口问:“为什么这么慢?”

我不准备告诉您正确的选择应该是什么,而给出一些理由来说明为什么 Web 服务可能是一个好的选择。

其中的一个理由可能是服务器端编程。即使是像 Web 服务这样好的机制,也仍然由于 XML 处理以及传输和接收 SOAP 消息的开销的原因而不能满足严格的实时处理需求。因此在设计时需要考虑两个问题:

  • 与普通的 HTTP 访问和专用的消息传递方法相比,每次 Web 服务调用的开销都比较大,所以当您主要考虑性能时,您可能需要首先选择另一项技术。
  • 由于开销的原因,如果您只需要在应用程序的各层之间进行通信,就不必选择 Web 服务。例如,不要将 Web 服务放在应用程序的视图层和控制器层之间。

现在,我假定您已经决定使用 Web 服务。那么,在总体设计方面需要考虑哪些问题?


设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图一)
设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二) 设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二)



在决定使用 Web 服务之后

您需要考虑的下一个问题是 Web 服务的总体设计。可以运用下列通用设计指导原则:

  • 管理 Web 服务的粒度。
  • 首先定义 Web 服务接口,然后加以实现。
  • 使用 Document/literal 作为编码样式。
  • 优先选择 JavaBean 组件而不是 Enterprise JavaBean (EJB) 组件作为服务提供者。
  • 避免 XML 元素嵌套太深,因为这可能大大延长解析、封送处理和取消封送处理的时间。

下面详细介绍这些设计考虑事项。

管理 Web 服务粒度

关于粒度管理需要谨记以下几点:

  • 始终优先考虑粗粒度的 Web 服务;决不要在分布式系统之间使用细粒度 Web 服务调用。
  • Web 服务是一个很好的工具集,但是当您以细粒度的方式使用 Web 服务时,它可能对应用程序的性能有很大的影响,因为 XML 解析、序列化和反序列化的开销很高;这种开销可能占处理时间的百分之三十。

在本文中,我以 Task Management 系统中的登录功能为例。驱动程序首先登录到系统,如果登录成功,则有两列任务显示在屏幕上。

对于此登录问题有两种解决方案:

  • 一种解决方案是首先调用登录方法,然后当该方法调用返回 true 时,调用一个方法来获取相应的任务。
  • 其他的事情将在一步中完成。如果驱动程序登录到 Task Management 系统,则返回驱动程序的角色以及分配的任务和启动的任务。如果登录失败,则返回指示驱动程序未登录到该系统的信息。

对于第一种解决方案,它是细粒度登录系统,而显示任务列表需要两次调用 Web 服务;这可能带来很大的延迟。第二种解决方案是粗粒度登录系统,它返回您在一次调用中需要的所有信息,并确保由于网络延迟和系统 I/O 带来的影响最小。

首先定义 Web 服务接口,然后进行实现。

这不仅从移动 Web 服务的角度来看是适用的,而且从 Web 服务设计和(更高的层次)面向对象的软件设计来看也是适用的。正如您所知道的,接口是客户端和服务提供程序之间的契约,而且保持稳定非常重要(因为正如您所知道的,实现容易改变)。

定义功能接口非常直接和直观——只需考虑:

  • 您的目标
  • 您需要获取什么信息
  • 您需要将何种结果返回到客户端

然后,您应该将该接口的相关要点写下来,并在这些要点的指导下完成所有实现细节。

这是一条简单的设计指导原则。了解您的目标非常重要,目标可以驱动您编写测试用例,并且指导您编写功能实现,这也是为什么测试驱动开发 (TDD) 广泛地应用于各种开发技术的原因。

使用 Document/literal 作为编码样式

目前,JAX-RPC 支持三种操作模式:

  • RPC/encoded。其优点在于简单,接收方可以轻松地将消息发送到操作的实现。其缺点在于类型编码信息的开销较大,这会降低吞吐量性能。
  • RPC/literal。其优点与 RPC/encoded 相同,而且 遵循 WS-I 组织制定的规范(请参阅参考资料)。
  • Document/literal。其优点在于没有类型编码信息,任何 XML 验证器都可以验证消息。其缺点在于 SOAP 消息中缺少操作名,所以发送消息很难甚至不可能。

在一些资料中,您还可能发现名为 Document/encoded(使用这种模式的人不多)和 Document/literal wrapped(由 Microsoft 定义,但是没有相关规范,其缺点在于比其他模式复杂)的模式。对这些操作模式的详细解释可以在参考资料中的“Which style of WSDL should I use?”内找到。

在这些模式中,WS-I 标准仅支持 RPC/literal 和 Document/literal。对于移动 Web 服务,JAX-RPC 实现必须使用 Document/literal 将基于 Web 服务描述语言 (WSDL) 的服务描述映射到相应的 Java 表示形式。因此,如果您只使用 Document/literal 作为编码样式,则您是最安全的

优先选择 JavaBeanser 组件而不是 EJB 组件作为服务提供程序

在使用 Java 编程语言公开服务时,需要考虑两种类型的服务提供程序:

  • 从 JavaBean 组件生成 Web 服务
  • 使用无状态会话 EJB 组件

虽然在某些情况下,EJB 组件非常有用,但是 JavaBeans 组件常常是更好的选择,特别是在开发移动 Web 服务时。实现使用 JavaBean 组件生成的服务提供程序比较简单和容易,而且与相应的会话 EJB 组件相比,JavaBean 组件更稳定。但是,如果您需要从使用 EJB 组件开发的现有 J2EE 应用程序公开 Web 服务,则请使用 EJB 组件。

避免 XML 元素嵌套太深

如果数组的数组、复杂类型的数组或包含另一个自定义复杂类型的复杂类型等嵌套太深,则将大大影响 Web 服务的性能。清单 1 显示一个 XML 描述示例——一个自定义数据类型 Task 类的数组:



清单 1. 自定义数据类型
import java.io.Serializable;

public class Task implements Serializable {
    /**
     * The id of the task
     */
    private int taskID = 0;

    /**
     * Owner name of the task
     */
    private String ownerName;

    /**
     * public default non-argument constrUCtor
     *  
     */
    public Task() {

    }

    /**
     * Constructor of the Task class
     * 
     * @param taskID
     *            id of the task
     * @param ownerName
     *            owner name of the task
     */
    public Task(int taskID, String ownerName) {
        this.taskID = taskID;
        this.ownerName = ownerName;
    }

    /**
     * @return Returns the ownerName.
     */
    public String getOwnerName() {
        return ownerName;
    }

    /**
     * @param ownerName
     *            The ownerName to set.
     */
    public void setOwnerName(String ownerName) {
        this.ownerName = ownerName;
    }

    /**
     * @return Returns the taskID.
     */
    public int getTaskID() {
        return taskID;
    }

    /**
     * @param taskID The taskID to set.
     */
    public void setTaskID(int taskID) {
        this.taskID = taskID;
    }
}


如果一个方法返回如 清单 1 中定义的 Task 的数组,则该方法的源代码包含在下面的清单中,方法 getTasks() 返回一个由五个 Task 对象组成的数组,如清单 2 所示。



清单 2. 返回自定义数据类型的数组的方法
    public Task[] getTasks(String name){
		Task[] tasks = new Task[5];

		for(int i=0; i<5; i++){
			tasks[i] = new Task(i, name);
		}
		return tasks;
	}

当使用 getTasks()(如清单 3 所示)所属的 JavaBean 组件公开 Web 服务时,Task 类映射到其中包含 Task 类的名称空间的 tn2:Task



清单 3. WSDL 定义中的 XML 数据类型
<complexType name="Task">
    <sequence
     <element name="ownerName" nillable="true" type="xsd:string"/>
     <element name="taskID" type="xsd:int"/>
    </sequence>
</complexType>

同时,数据类型 Task[] 映射到 ArrayOf_tn2_TaskArrayOf_tn2_Task 的 XML 描述如清单 4 所示:



清单 4. ArrayOf_tn2_Task 的 XML 描述
<complexType name="ArrayOf_tns2_Task">
    <sequence>
     <element maxOccurs="unbounded" minOccurs="0" name="Task" 
       nillable="true" type="tns2:Task"/>
    </sequence>
   </complexType>

清单 4 所示,为单个自定义复杂类型数组生成的 XML 描述很长。相反,Java 语言中的单个 String 类型映射到 xsd:string,而没有生成 complexType 元素;诸如 booleanintbyte 这样的基元类型分别映射到 xsd:booleanxsd:intxsd:byte

您可能已经注意到 XML 元素的嵌套(避免嵌套太深)和粒度考虑(使用粗粒度)之间的冲突。在实际运用中,嵌套和粒度之间应该有一个平衡。如果您更关注应用程序的性能,则应该仔细地权衡这两个考虑事项,以获得一个更好的解决方案。


设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图一)
设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二) 设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二)


移动 Web 服务的设计考虑事项

我已经讨论了设计 Web 服务的指导原则,现在我将把重点放在移动 Web 服务的考虑事项上。在大多数情况下,当将 JAX-RPC 值类型用于移动 Web 服务时需要考虑一些事情。JAX-RPC 值类型(遵循 JSR-101)是 Java 类,其值可以在服务客户端和服务端点之间移动。为了获得一致的值类型,必须遵循一系列规则。我只列出其中的几条,与本文关系最大的规则是:

  • 您必须具有公共缺省构造器。
  • 您必须具有用于需要在网络上传输的字段的 setter 和 getter 方法。
  • 在处理数据集合时您应该使用数组。
  • 移动 Web 服务中有一些首选的数据类型。
  • 在处理输入和输出参数时注意可能出现的问题。

您必须具有公共缺省构造器

在反序列化的过程中,SOAP 运行时环境使用缺省构造器来构造对象。如果您试图在没有公共缺省构造器的情况下编写值类型(也称为数据传输对象),在当 JAX-RPC 运行时尝试序列化和反序列化数据对象时可能会遇到错误。对于像 IBM Rational® application Developer (RAD) 6.0 这样的 IDE,将不为该数据类型生成序列化和反序列化 Helper 类(由 RAD 通过前缀 _Helper_Ser_Deser 生成),所以在调用与自定义数据类型相关的方法时会出现序列化错误。不带参数的构造器确保可以根据序列化状态远程构造对象。

您必须具有用于网络传输字段的 setter 和 getter 方法


首先,看一看清单 5 中的类 FailTask 的源代码:



清单 5. FailTask 类的定义
public class FailTask {
    /**
     * The owner of the task
     */
    private int ownerid;
    /**
     * The name of the task
     */
    private String name;
    
    /**
     * Default public non-argument constructor 
     *
     */
    public FailTask(){
    	
    }
    /**
     * Constructor of FailTask class
     * @param ownerid Owner of the task
     * @param name Name of the task
     */
    public FailTask(int ownerid, String name){
    	this.ownerid = ownerid;
    	this.name = name;
    }
    
    /**
     * Getter method
     * @return the ownerid of the task
     */
    public int getOwnerid(){
    	return ownerid;
    }
    
    /**
     * Setter method
     * @param ownerid the ownerid to be set
     */
    public void setOwnerid(int ownerid){
    	this.ownerid = ownerid;
    }
  }

您可以将清单 6 中所示的方法添加到 Web 服务中,该方法将返回单个 FailTask 对象。



清单 6. 返回 FailTask 对象的方法
public FailTask getFailTask(int ownerid, String name){
		return new FailTask(ownerid, name);
	}

当使用 RAD 6.0 附带的 Universal Test Client 中的 1Rachel 参数调用 getFailTask() 方法时,所得到的响应如图 1 中所示。



图 1. Universal Test Client 中的响应视图
设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图三)

name 字段在哪里?它不在这里,因为我没有通过 getter 和 setter 方法提供 name 字段。Setter 和 getter 方法是必须提供的两个方法。和 FailTask_Ser 类中一样,name 字段 getter 方法用于将 name 字段值写入 SOAP 消息。在 FailTask_Deser 类中,name 字段 setter 方法用于设置反序列化的 FailTask 对象的 name 值。

在处理数据集合时您应该使用数组

为了有效地使用 Web 服务,您必须或多或少地使用数据集合。但是,必须提醒:当处理许多值类型时,事情会变得比较麻烦,因此需要考虑以下问题。

当需要动态长度的数组时,请考虑 ArrayList。您已经反复听说过,如果不考虑同步,则 ArrayListVector 更有效。但遗憾的是,JSR-101 JAX-RPC 规范没有强制要求支持 Java Collection 类型。有些 Web 服务引擎可能没有为 ArrayList 提供支持。例如,IBM Web 服务引擎只正式支持 Java Collection Framework 中的一小部分类,包括 java.util.Vectorjava.util.HashTablejava.util.HashMap

那么,尝试一下另一个动态数组 Vector 会如何呢?如果在相同平台上生成存根文件,它将正常工作。但是,如果在不同的平台上生成存根文件,则将遇到一些问题。例如,在 Web 服务描述语言 (WSDL) 文件中,Vector 或其他 Collection 类型映射到 ArrayOfAnyType。其他平台可能不知道将其映射到哪个 Collection 类型,而且 Vector 中包含的数据元素也映射到 WSDL 中的 AnyType。(这里存在的一个大问题是,其他的平台不知道 AnyType 代表什么类型。)有关该主题的详细信息,请参阅参考资料中的“ Web services programming tips and tricks: Improve the interOperability between J2EE and .NET ”。

使用数组的最后一个原因是,移动 Web 服务不支持 Java Collection 类型,这使得所有其他的解释都显得没有必要。这意味着您可能无法从形式良好的 WSDL 文件为移动 Web 服务生成存根文件。

移动 Web 服务中的首选的一些数据类型


使用基元类型 long 传输 Date 或 Calendar 表示形式

对于标准 JAX-RPC 运行时实现,有两种支持的标准类型映射:

  • Java 类型到 XML 数据类型
  • XML 数据类型到 Java 类型

在 JAX-RPC 子集规范中,只需要第二种映射。表 1 显示了从支持的 XML 数据类型到 Java 类型的映射的简要列表;有关详细信息,请参阅 JSR-172。



表 1. 从 XML 数据类型到 Java 类型的映射
简单 XML 类型 Java 类型 xsd:string java.lang.String xsd:int int xsd:long long xsd:short short xsd:boolean boolean xsd:byte byte xsd:float java.lang.Stringfloat xsd:double java.lang.Stringdouble xsd:QName javax.xml.namespace.QName xsd:base64Binary byte[] xsd:hexBinary byte[]

表 1 中您可以清楚地看出,该列表中不存在像 xsd:dateTimexsd:datexsd:time 这样的元素,而在标准 JAX-RPC 规范中,这三个元素确实是映射到 java.util.Calendar 的 XML 类型。请注意,在 JAX-RPC1.1 中定义的 Java 据类型映射到 XML 类型的映射中,java.util.Date 映射到 xsd:dateTime

那么,在尝试传输日期或时间表示形式时,您应该使用什么?改为使用 long 类型的时间。long 类型的日期格式与不同时区的时间表示形式无关,并且因为它是基元类型,所以比其他类型的 Java 对象更有效。

注意 float 和 double 类型的使用

首先需要注意的一点是,正如您所知,CLDC 1.0 (Connected Limited Device Configuration) 并没有出于性能的原因而提供 float 和 double 本机类型,即使 CLDC 1.1 和 CDC 都为其提供了支持。那么,如果您必须使用针对 CLDC 1.0 的 Web 服务,您该如何做呢?JSR-172 为您提供了部分答案。

为了在 CLDC 1.0 中缺省支持 xsd:floatxsd:double,实现必须 生成代码来将这些类型映射到 java.lang.String。为了支持为 float 和 double 提供本机支持的配置和平台(CLDC 1.1 和 CDC),存根生成器实现也必须 生成代码来将这些类型映射到适当的本机 Java 类型。(详细信息,请参阅参考资料,以获得指向 JSR-172: J2ME Web 服务规范的链接。)

我将演示一个添加两个 float 数的简单 Web 服务(清单 7)。



清单 7. 添加两个 float 数
public class TaskWs {
    public TaskWs() {

    }

    /** 
     * Adding two float numbers and return their sum
     * @param a First number to add,
     * @param b Second number to add
     * @return The sum of a and b.
     */
    public float addTwo(float a, float b) {
        return a + b;
    }

}
}

所生成的 WSDL 定义中的 XML 数据类型定义如清单 8 所示。



清单 8. 与清单 7 对应的 WSDL 定义
 <wsdl:types>
  <schema targetNamespace="http://ws.test.ibm.com" 
    xmlns="http://www.w3.org/2001/XMLSchema" 
  xmlns:impl="http://ws.test.ibm.com" xmlns:intf="http://ws.test.ibm.com" 
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <element name="addTwoResponse">
     <complexType>
      <sequence>
       <element name="addTwoReturn" type="xsd:float"/>
      </sequence>
     </complexType>
    </element>
    <element name="addTwo">
     <complexType>
      <sequence>
       <element name="a" type="xsd:float"/>
       <element name="b" type="xsd:float"/>
       <element name="b" type="xsd:float"/>
      </sequence>
     </complexType>
    </element>
  </schema>
  </wsdl:types>


对于针对 CLDC 1.0 的 Web 服务客户端,所生成的存根如清单 9 所示。



清单 9. 为 CLDC 1.0 生成的客户端存根
 public interface TaskWs extends java.rmi.Remote {
    public java.lang.String addTwo(java.lang.String _a, java.lang.String _b)
            throws java.rmi.RemoteException, javax.xml.rpc.JAXRPCException;
}

所以,当调用 CLDC 1.0 中的 Web 服务时,您必须使用 addTwo() 方法提供两个 String 参数,而对于针对平台 CLDC 1.1 的 Web 服务客户端,所生成的服务接口与清单 10 中所描述的类似:



清单 10. 为 CLDC 1.1 生成的客户端存根
public interface TaskWs extends java.rmi.Remote {
    public float addTwo(float _a, float _b) 
    throws java.rmi.RemoteException, javax.xml.rpc.JAXRPCException;
}

这将 xsd:float 映射到所生成的客户端存根中的 float 类型。看到 CLDC 1.0 和 CLDC 1.1 之间的不同之处了吗?

在为移动设备开发 Web 服务时,请注意 float 和 double 类型,因为 CLDC 1.0 虚拟机实现无法加载为 CLDC 1.1 生成的存根(使用到 float 和 double 的本机映射)。同时针对 CLDC 1.0 和 CLDC 1.1 的 Java 2 Platform Micro Edition (J2ME) 应用程序的开发人员应该使用到 java.lang.String 的缺省映射,以获得最好可重用性。

在处理输入和输出参数时注意可能出现的问题

JSR-172 指定了以副本的形式传送并以副本的形式创建返回值的所有参数。但是,当处理数据集合时,零数组 (返回零)和空数组 (返回其本身)需要密切关注。

我的建议是尽可能地避免使用空数组。当处理移动 Web 服务时,空数组可能是一个问题。

假定您需要返回任务对象数组。原始代码如清单 11 所示。



清单 11. 一个简单的值对象
public class SimpleTask {
    /**
     * The name of the task
     */
    private String name;

    /**
     * The default constructor
     *
     */
    public SimpleTask() {

    }

    /**
     * @return Returns the name of the task.
     */
    public String getName() {
        return name;
    }

    /**
     * @param name
     *            The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }
}

Web 服务实现如清单 12 所示。



清单 12. 返回值对象的数组的方法
1  public SimpleTask[] getSimpleTasks(){
2       SimpleTask[] tasks = null;
3       /*
4        * Your code dealing with DB goes here
5        * ....
6        * tasks = ...
7        */
8       return tasks;
9	}

当生成 Web 服务存根和使用生成的存根测试 Web 服务时,本例中的每一个部分都将正常工作。但是,因为在结束一个阶段之前您需要使用 Jtest 进行完整的代码检查,所以当您对代码片段运行 Jtest 时,您将看到一条建议:“Return zero-length arrays instead of null”。在犹豫片刻之后后,您将赞同 Jtest 的建议。如果您返回零数组,该代码的客户端必须编写额外的代码来检查返回值是否为零(如清单 13 所示)。



清单13. 用于调用 Web 服务的客户端代码片段
   SimpleTask[] tasks = service.getSimpleTasks();
       if(tasks != null){
           int length = tasks.length;
           //do something here
       }

当您将 SimpleTask[] tasks = null;清单 12 中的第 2 行)修改为 SimpleTask[] tasks = new SimpleTasks[0]; 时,您只需将清单 13 编写为:

   SimpleTask[] tasks = service.getSimpleTasks();
           int length = tasks.length;


在修改之后,您会认为代码逻辑没有更改,并再次运行客户端来调用 Web 服务,但是现在却引发了异常。到目前为止,您已经根据 Jtest 的建议做了许多小的修补——您忘记修改了什么——这可能导致需要花额外的时间来努力找到发生错误的原因。这个过程真的漫长而乏味。

那么,问题究竟出在什么地方呢?一般来说,对于零对象数组(如 SimpleTask),返回的 SOAP 消息如清单 14 所示。



清单 14. 返回零数组时的 SOAP 消息
<soapenv:Body>
<p147:getSimpleTasksResponse xmlns:p147="http://ws.test.ibm.com">
  <getSimpleTasksReturn xsi:nil="true" />
</p147:getSimpleTasksResponse>
</soapenv:Body>

对于空数组(如 SimpleTask[] tasks = new SimpleTask[0]),SOAP 消息如清单 15 所示。



清单 15. 返回空数组时的 SOAP 消息
<soapenv:Body>
<p147:getSimpleTasksResponse xmlns:p147="http://ws.test.ibm.com">
  <getSimpleTasksReturn />
 </p147:getSimpleTasksResponse>
 </soapenv:Body>

其不同之处在于 <getSimpleTasksReturn/><getSimpleTasksReturn xsi:nil = true> 之间。图 2 说明了空数组参数大部分时间是无效的。对于自定义的数据类型(包括另一个自定类型的数组),不要将类变量初始化为空数组——相反,要将其初始化为零数组,尽管所生成的空数组和零数组的 WSDL 定义是相同的。



图 2. 根据 JSR-172 编码零数组参数和空数组参数
设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图四)

设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图一)
设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二) 设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二)


结束语

在处理移动 Web 服务时,您需要更加谨慎,因为移动 Web 服务规范只支持部分 API。如果您计划开发移动 Web 服务,则当您处理值类型和集合类型时,我向您介绍了一些窍门。此外,我还提供了以下信息:

  • 设计前考虑事项,例如何时使用 Web 服务,以及如何分析使用 Web 服务、套接字或消息传递技术之间的利弊。
  • 在决定使用 Web 服务后需要考虑的设计事项,包括:
    1. 管理粒度
    2. 设计 Web 服务接口
    3. 使用 Document/literal 作为编码样式
    4. 为什么您应该使用 JavaBeans 组件而不是 EJB 技术作为您的服务提供程序
    5. 为什么您应该避免 XML 元素嵌套太深
    6. 嵌套和粒度之间的平衡
  • 在使用 JAX-RPC 值类型时需要考虑的移动 Web 服务设计事项,包括:
    1. 公共缺省构造器
    2. Setter 和 getter 方法
    3. 使用数组类型而不是 Java Collection 类型
    4. 确定首选数据类型
    5. 处理输入和输出参数

设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图一)
设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二) 设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二)


参考资料

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

  • "Web 服务编程技巧及窍门: 改善 J2EE 与 .NET 之间的互操作性(developerWorks,2005 年 1 月)向您介绍在处理标准 Web 服务时如何管理集合、数组和原型数据类型。

  • JSR-172:J2ME Web 服务规范定义了一个可选的包,其提供了从 J2ME 到 Web 服务的标准访问。

  • "我应该采用哪一种 WSDL 样式?" (developerWorks,2005 年 5 月)描述了本文所提供的操作模式(以及另外两种模式):RPC/encoded、RPC/literal、Document/encoded、Document/literal 和 Document/literal wrapped 模式。

  • Web Services - Interoperability Organization (WS-I) 是一个开放的行业组织,旨在促进跨平台、操作系统和编程语言之间的互操作。

  • "Tips and tricks: XML does the job(developerWorks,2002 年 3 月)说明了如何使用 XML-RPC 来定义移动 Web 服务客户端。

  • "Cross-platform programming with Java technology and the IBM Web Services Toolkit for Mobile Devices(developerWorks,2003 年 2 月)帮助确保您的 Java 应用程序在不需要修改的情况下作为尽可能多的平台运行。

  • JSR-101: Java APIs for XML-based RPC 讨论了如何使用 JAX-RPC 值类型。

  • "Using Mobile Devices with the WSTK(developerWorks,2002 年 12 月)说明了 Web Services Tool Kit for Mobile Devices 如何帮助开发在小型移动设备上使用 Web 服务的应用程序。

  • "交付 Web 服务至移动式应用程序(developerWorks,2003 年 1 月)说明了如何使用支持 J2ME 的移动设备和 kSOAP 库访问 Web 服务。

  • "为移动设备开发 Web 服务客户端 (developerWorks,2003 年 3 月)指导您完成构建 J2ME MIDP 设备上的移动 Web 服务客户端的必要步骤。

  • Connected Limited Device Configuration 1.0(CLDC;JSR-30 和 JSR-139)定义了用于资源约束型设备的一组基本 API 和虚拟设备。提供了一个功能强大的 Java 平台,用于开发与 Mobile Information Device Profile (MIDP) 结合在具有有限内存、处理能力和图形功能的设备上运行的应用程序。

  • Best practices for Web services 系列(developerWorks,2002 年 10 月)详细介绍了 Web 服务设计考虑事项。

  • 请查阅 Safari eReference Bookstore,以找到许多特定于移动和其他技术的主题。

  • developerWorks Wireless technology 专区专门发布有关基于移动和普及计算的解决方案的各方面的文章。


获得产品和技术
  • Jtest 可以自动完成 Java 单元测试,从正在运行的应用程序自动生成 JUnit 测试用例,以及测试单个类或大型的复杂应用程序。

  • Web Services Tool Kit for Mobile Devices (alphaWorks) 是成熟的技术——弄清楚该技术的应用领域。


设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图一)
设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二) 设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二)



关于作者

设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二)

设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图五)

设计移动 Web 服务 在设计移动 Web 服务时需要考虑的三个设计层次(图二)

Shu Fang Rui 毕业于中国上海交通大学。她对无线技术和 Web 服务非常感兴趣。除了旅行之外,她还喜欢从事一些运动。

(出处:http://www.cncms.com)


Tags:设计 移动 Web

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接