﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-ahwen-文章分类-AJAX技术</title><link>http://www.blogjava.net/ahwen/category/7720.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 08:19:53 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 08:19:53 GMT</pubDate><ttl>60</ttl><item><title>面向 Java 开发人员的 Ajax: 结合 Direct Web Remoting 使用 Ajax</title><link>http://www.blogjava.net/ahwen/articles/31972.html</link><dc:creator>ahwen</dc:creator><author>ahwen</author><pubDate>Wed, 22 Feb 2006 07:02:00 GMT</pubDate><guid>http://www.blogjava.net/ahwen/articles/31972.html</guid><wfw:comment>http://www.blogjava.net/ahwen/comments/31972.html</wfw:comment><comments>http://www.blogjava.net/ahwen/articles/31972.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ahwen/comments/commentRss/31972.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ahwen/services/trackbacks/31972.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1><SPAN style="COLOR: #999999">面向 Java 开发人员的 Ajax: </SPAN>结合 Direct Web Remoting 使用 Ajax</H1>
<P id=subtitle>数据序列化不可能比这更简单了！</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/cn/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="虽然令人兴奋，但是把 Ajax 功能添加到应用程序可能意味着大量艰苦的工作。在“面向 Java 开发人员的 Ajax”系列的第 3 篇文章中，Philip McCarthy 介绍了如何使用Direct Web Remoting（DWR）直接把 JavaBean 的方法公开给 JavaScript 代码并自动进行 Ajax 的繁重工作。" name=body><INPUT type=hidden value="面向 Java 开发人员的 Ajax: 结合 Direct Web Remoting 使用 Ajax" name=subject><INPUT type=hidden value=cn name=lang>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT color=#5c81a7>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img src="//www.ibm.com/i/c.gif" height="16" width="16" alt="" /></td><td width="122" class="small"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw_bold.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:void forumWindow()"><B><FONT color=#5c81a7>讨论</FONT></B></A></P></TD></TR>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/dn.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#download"><B><FONT color=#996699>样例代码</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#rate"><B><FONT color=#996699>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 中级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#author"><FONT color=#996699>Philip McCarthy </FONT></A>, 软件开发顾问, Independent Consultant<BR></P>
<P>2005 年 12 月 27 日</P>
<BLOCKQUOTE>虽然令人兴奋，但是把 Ajax 功能添加到应用程序可能意味着大量的艰苦工作。在<I>面向 Java® 开发人员的 Ajax </I>系列的第 3 篇文章中，Philip McCarthy 介绍了如何使用Direct Web Remoting（DWR）直接把 JavaBean 的方法公开给 JavaScript 代码并自动进行 Ajax 的繁重工作。 </BLOCKQUOTE>
<P>理解 <A href="http://www.ibm.com/developerworks/cn/java/j-ajax1/"><FONT color=#996699>Ajax 编程的基本知识</FONT></A> 是重要的，但是如果正在构建复杂的用户界面，那么能够在更高层次的抽象上工作也很重要。在<I>面向 Java 开发人员的 Ajax </I>系列的第 3 篇文章中，我在上个月的 <A href="http://www.ibm.com/developerworks/cn/java/j-ajax2/"><FONT color=#996699>Ajax 的数据序列化技术</FONT></A> 基础之上，介绍一种可以避免繁琐的 Java 对象序列化细节的技术。</P>
<P>在 <A href="http://www.ibm.com/developerworks/cn/java//j-ajax2/"><FONT color=#996699>上一篇文章</FONT></A> 中，我介绍了如何用 JavaScript 对象标注（JSON）以一种在客户机上容易转化成 JavaScript 对象的格式对数据进行序列化。有了这个设置，就可以用 JavaScript 代码调用远程服务，并在响应中接收 JavaScript 对象图，但是又不像远程过程调用。这一次，将学习如何更进一步，使用一个框架，把从 JavaScript 客户代码对服务器端 Java 对象进行远程调用的能力正式化。</P>
<P>DWR 是一个开放源码的使用 Apache 许可协议的解决方案，它包含服务器端 Java 库、一个 DWR servlet 以及 JavaScript 库。虽然 DWR 不是 Java 平台上唯一可用的 Ajax-RPC 工具包，但是它是最成熟的，而且提供了许多有用的功能。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#resources"><FONT color=#996699>参考资料</FONT></A>，在继续学习之前下载 DWR。</P>
<P><A name=IDAUT0KB><SPAN class=atitle><FONT face=Arial size=4>DWR 是什么？</FONT></SPAN></A></P>
<P>从最简单的角度来说，DWR 是一个引擎，可以把服务器端 Java 对象的方法公开给 JavaScript 代码。使用 DWR 可以有效地从应用程序代码中把 Ajax 的全部请求-响应循环消除掉。这意味着客户端代码再也不需要直接处理 <CODE>XMLHttpRequest</CODE> 对象或者服务器的响应。不再需要编写对象的序列化代码或者使用第三方工具才能把对象变成 XML。甚至不再需要编写 servlet 代码把 Ajax 请求调整成对 Java 域对象的调用。</P>
<P>DWR 是作为 Web 应用程序中的 servlet 部署的。把它看作一个黑盒子，这个 servlet 有两个主要作用：首先，对于公开的每个类，DWR 动态地生成包含在 Web 页面中的 JavaScript。生成的 JavaScript 包含存根函数，代表 Java 类上的对应方法并在幕后执行 <CODE>XMLHttpRequest</CODE>。这些请求被发送给 DWR，这时它的第二个作用就是把请求翻译成服务器端 Java 对象上的方法调用并把方法的返回值放在 servlet 响应中发送回客户端，编码成 JavaScript。DWR 还提供了帮助执行常见的用户界面任务的 JavaScript 工具函数。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAFU0KB><SPAN class=atitle><FONT face=Arial size=4>关于示例</FONT></SPAN></A></P>
<P>在更详细地解释 DWR 之前，我要介绍一个简单的示例场景。像在前一篇文章中一样，我将采用一个基于在线商店的最小模型，这次包含一个基本的产品表示、一个可以包含产品商品的用户购物车以及一个从数据存储查询产品的数据访问对象（DAO）。<CODE>Item</CODE> 类与前一篇文章中使用的一样，但是不再实现任何手工序列化方法。图 1 说明了这个简单的设置：</P><BR><A name=figure1><B>图 1. 说明 Cart、CatalogDAO 和 Item 类的类图</B></A><BR><IMG height=280 alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/dwr-fig1.gif" width=450><BR>
<P>在这个场景中，我将演示两个非常简单的用例。第一，用户可以在目录中执行文本搜索并查看匹配的商品。第二，用户可以添加商品到购物车中并查看购物车中商品的总价。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDA2U0KB><SPAN class=atitle><FONT face=Arial size=4>实现目录</FONT></SPAN></A></P>
<P>DWR 应用程序的起点是编写服务器端对象模型。在这个示例中，我从编写 DAO 开始，用它提供对产品目录数据存储的搜索功能。<CODE>CatalogDAO.java</CODE> 是一个简单的无状态的类，有一个无参数的构造函数。清单 1 显示了我想要公开给 Ajax 客户的 Java 方法的签名：</P><BR><A name=listing1><B>清单 1. 通过 DWR 公开的 CatalogDAO 方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/**
 * Returns a list of items in the catalog that have 
 *  names or descriptions matching the search expression
 * @param expression Text to search for in item names 
 *  and descriptions 
 * @return list of all matching items
 */
public List&lt;Item&gt; findItems(String expression);

/**
 * Returns the Item corresponding to a given Item ID
 * @param id The ID code of the item
 * @return the matching Item
 */
public Item getItem(String id);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>接下来，我需要配置 DWR，告诉它 Ajax 客户应当能够构建 <CODE>CatalogDAO</CODE> 并调用这些方法。我在清单 2 所示的 dwr.xml 配置文件中做这些事：</P><BR><A name=listing2><B>清单 2. 公开 CatalogDAO 方法的配置</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;!DOCTYPE dwr PUBLIC
  "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
  "http://www.getahead.ltd.uk/dwr/dwr10.dtd"&gt;
&lt;dwr&gt;
  &lt;allow&gt;
    &lt;create creator="new" javascript="catalog"&gt;
      &lt;param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/&gt;
      &lt;include method="getItem"/&gt; 
      &lt;include method="findItems"/&gt; 
    &lt;/create&gt; 
    &lt;convert converter="bean" 
      match="developerworks.ajax.store.Item"&gt;
      &lt;param name="include" 
        value="id,name,description,formattedPrice"/&gt;
    &lt;/convert&gt;
  &lt;/allow&gt;
&lt;/dwr&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>dwr.xml 文档的根元素是 <CODE>dwr</CODE>。在这个元素内是 <CODE>allow</CODE> 元素，它指定 DWR 进行远程的类。<CODE>allow</CODE> 的两个子元素是 <CODE>create</CODE> 和 <CODE>convert</CODE>。</P>
<P><A name=IDARW0KB><SPAN class=smalltitle><STRONG><FONT face=Arial>create 元素</FONT></STRONG></SPAN></A></P>
<P><CODE>create</CODE> 元素告诉 DWR 应当公开给 Ajax 请求的服务器端类，并定义 DWR 应当如何获得要进行远程的类的实例。这里的 <CODE>creator</CODE> 属性被设置为值 <CODE>new</CODE>，这意味着 DWR 应当调用类的默认构造函数来获得实例。其他的可能有：通过代码段用 Bean 脚本框架（Bean Scripting Framework，BSF）创建实例，或者通过与 IOC 容器 Spring 进行集成来获得实例。默认情况下，到 DWR 的 Ajax 请求会调用 <CODE>creator</CODE>，实例化的对象处于页面范围内，因此请求完成之后就不再可用。在无状态的 <CODE>CatalogDAO</CODE> 情况下，这样很好。</P>
<P><CODE>create</CODE> 的 <CODE>javascript</CODE> 属性指定从 JavaScript 代码访问对象时使用的名称。嵌套在 <CODE>create</CODE> 元素内的 <CODE>param</CODE> 元素指定 <CODE>creator</CODE> 要创建的 Java 类。最后，<CODE>include</CODE> 元素指定应当公开的方法的名称。显式地说明要公开的方法是避免偶然间允许访问有害功能的良好实践 —— 如果漏了这个元素，类的所有方法都会公开给远程调用。反过来，可以用 <CODE>exclude</CODE> 元素指定那些想防止被访问的方法。</P>
<P><A name=IDALA0W><SPAN class=smalltitle><STRONG><FONT face=Arial>convert 元素</FONT></STRONG></SPAN></A></P>
<P><CODE>creator</CODE> 负责公开用于 Web 远程的类和类的方法，<CODE>convertor</CODE> 则负责这些方法的参数和返回类型。<CODE>convert</CODE> 元素的作用是告诉 DWR 在服务器端 Java 对象表示和序列化的 JavaScript 之间如何转换数据类型。</P>
<P>DWR 自动地在 Java 和 JavaScript 表示之间调整简单数据类型。这些类型包括 Java 原生类型和它们各自的类表示，还有 String、Date、数组和集合类型。DWR 也能把 JavaBean 转换成 JavaScript 表示，但是出于安全性的原因，做这件事要求显式的配置。</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#listing2"><FONT color=#996699>清单 2</FONT></A> 中的 <CODE>convert</CODE> 元素告诉 DWR 用自己基于反射的 bean 转换器处理 <CODE>CatalogDAO</CODE> 的公开方法返回的 <CODE>Item</CODE>，并指定序列化中应当包含 <CODE>Item</CODE> 的哪个成员。成员的指定采用 JavaBean 命名规范，所以 DWR 会调用对应的 <CODE>get</CODE> 方法。在这个示例中，我去掉了数字的 <CODE>price</CODE> 字段，而是包含了 <CODE>formattedPrice</CODE> 字段，它采用货币格式进行显示。</P>
<P>现在，我准备把 dwr.xml 部署到 Web 应用程序的 <CODE>WEB-INF</CODE> 目录，在那里 DWR servlet 会读取它。但是，在继续之前，确保每件事都按照希望的那样运行是个好主意。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAFC0W><SPAN class=atitle><FONT face=Arial size=4>测试部署</FONT></SPAN></A></P>
<P>如果 <CODE>DWRServlet</CODE> 的 <CODE>web.xml</CODE> 定义把 <CODE>init-param</CODE><CODE> debug</CODE> 设置为 <CODE>true</CODE>，那么就启用了 DWR 非常有帮助的测试模式。导航到 <CODE>/{your-web-app}/dwr/</CODE> 会把 DWR 配置的要进行远程的类列表显示出来。在其中点击，会进入指定类的状态屏幕。<CODE>CatalogDAO</CODE> 的 DWR 测试页如图 2 所示。除了提供粘贴到 Web 页面的 <CODE>script</CODE> 标记（指向 DWR 为类生成的 JavaScript）之外，这个屏幕还提供了类的方法列表。这个列表包括从类的超类继承的方法，但是只有在 <CODE>dwr.xml</CODE> 中显式地指定为远程的才标记为可访问。</P><BR><A name=figure2><B>图 2. CatalogDAO 的 DWR 测试页</B></A><BR><IMG height=415 alt="DWR 为 CatalogDAO 生成的诊断和测试页" src="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/dwr-fig2.jpg" width=460><BR>
<P>可以在可访问的方法旁边的文本框中输入参数值并点击 <B>Execute</B> 按钮调用方法。服务器的响应将在警告框中用 JSON 标注显示出来，如果是简单值，就会内联在方法旁边直接显示。这个测试页非常有用。它们不仅允许检查公开了哪个类和方法用于远程，还可以测试每个方法是否像预期的那样工作。</P>
<P>如果对远程方法的工作感到满意，就可以用 DWR 生成的 JavaScript 存根从客户端代码调用服务器端对象。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDA5D0W><SPAN class=atitle><FONT face=Arial size=4>调用远程对象</FONT></SPAN></A></P>
<P>远程 Java 对象方法和对应的 JavaScript 存根函数之间的映射很简单。通用的形式是 <CODE>JavaScriptName.methodName(methodParams ..., callBack)</CODE>，其中 <CODE>JavaScriptName</CODE> 是 <CODE>creator</CODE> 的 <CODE>javascript</CODE> 属性指定的名称，<CODE>methodParams</CODE> 代表 Java 方法的 <I>n</I> 个参数，<CODE>callback</CODE> 是要用 Java 方法的返回值调用的 JavaScript 函数。如果熟悉 Ajax，可以看出这个回调机制是 <CODE>XMLHttpRequest</CODE> 异步性的常用方式。</P>
<P>在示例场景中，我用清单 3 中的 JavaScript 函数执行搜索，并用搜索结果更新用户界面。这个清单还使用来自 DWR 的 <CODE>util.js</CODE> 的便捷函数。要特别说明的是名为 <CODE>$()</CODE> 的 JavaScript 函数，可以把它当作 <CODE>document.getElementById()</CODE> 的加速版。录入它当然更容易。如果您使用过 JavaScript 原型库，应当熟悉这个函数。</P><BR><A name=listing3><B>清单 3. 从客户机调用远程的 findItems()</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/*
 * Handles submission of the search form
 */
function searchFormSubmitHandler() {

  // Obtain the search expression from the search field
  var searchexp = $("searchbox").value;

  // Call remoted DAO method, and specify callback function
  catalog.findItems(searchexp, displayItems);

  // Return false to suppress form submission
  return false;
}
       
/*
 * Displays a list of catalog items
 */
function displayItems(items) {

  // Remove the currently displayed search results
  DWRUtil.removeAllRows("items");

  if (items.length == 0) {
    alert("No matching products found");
    $("catalog").style.visibility = "hidden";
  } else {

    DWRUtil.addRows("items",items,cellFunctions);
    $("catalog").style.visibility = "visible";
  }
}</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在上面的 <CODE>searchFormSubmitHandler()</CODE> 函数中，我们感兴趣的代码当然是 <CODE>catalog.findItems(searchexp, displayItems);</CODE>。这一行代码就是通过网络向 DWR servlet 发送 <CODE>XMLHttpRequest</CODE> 并用远程对象的响应调用 <CODE>displayItems()</CODE> 函数所需要的全部内容。</P>
<P><CODE>displayItems()</CODE> 回调本身是由一个 <CODE>Item</CODE> 数组表示调用的。这个数组传递给 <CODE>DWRUtil.addRows()</CODE> 便捷函数，同时还有要填充的表的 ID 和一个函数数组。表中每行有多少单元格，这个数组中就有多少个函数。按照顺序使用来自数组的 <CODE>Item</CODE> 逐个调用每个函数，并用返回的内容填充对应的单元格。</P>
<P>在这个示例中，我想让商品表中的每一行都显示商品的名称、说明和价格，并在最后一列显示商品的 <B>Add to Cart</B> 按钮。清单 4 显示了实现这一功能的单元格函数数组：</P><BR><A name=listing4><B>清单 4. 填充商品表的单元格函数数组</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/*
 * Array of functions to populate a row of the items table
 * using DWRUtil's addRows function
 */
var cellFunctions = [
  function(item) { return item.name; },
  function(item) { return item.description; },
  function(item) { return item.formattedPrice; },
  function(item) {
    var btn = document.createElement("button");
    btn.innerHTML = "Add to cart";
    btn.itemId = item.id;
    btn.onclick = addToCartButtonHandler;
    return btn;
  }
];
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>前三个函数只是返回 dwr.xml 中 <CODE>Item</CODE> 的 <CODE>convertor</CODE> 包含的字段内容。最后一个函数创建一个按钮，把 <CODE>Item</CODE> 的 ID 赋给它，并指定在点击按钮时应当调用名为 <CODE>addToCartButtonHandler</CODE> 的函数。这个函数是第二个用例的入口点：向购物车中添加 <CODE>Item</CODE>。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDA3H0W><SPAN class=atitle><FONT face=Arial size=4>实现购物车</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><FONT face=Arial size=4><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></FONT></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=IDAII0W><B>DWR 的安全性</B></A><BR>
<P>DWR 设计时就考虑了安全性。使用 dwr.xml 明确地列出那些想做远程处理的类和方法，可以避免意外地把那些可能被恶意利用的功能公开出去。除此之外，使用调试测试模式，可以容易地审计所有公开到 Web 上的类和方法。</P>
<P>DWR 也支持基于角色的安全性。通过 bean 的 <CODE>creator</CODE> 配置，可以指定用户访问特定 bean 所必须属于的 J2EE 角色。通过部署多个 URL 受保护的 <CODE>DWRServlet</CODE> 实例，每个实例都有自己的 dwr.xml 配置文件，也可以提供拥有不同远程功能的用户集。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P>用户购物车的 Java 表示基于 <CODE>Map</CODE>。当 <CODE>Item</CODE> 添加到购物车中时，<CODE>Item</CODE> 本身作为键被插入 <CODE>Map</CODE>。 <CODE>Map</CODE> 中对应的值是一个 <CODE>Integer</CODE>，代表购物车中指定 <CODE>Item</CODE> 的数量。所以 <CODE>Cart.java</CODE> 有一个字段 <CODE>contents</CODE>，声明为 <CODE>Map&lt;Item,Integer&gt;</CODE>。</P>
<P>使用复杂类型作为哈希键给 DWR 带来一个问题 —— 在 JavaScript 中，数组的键必须是标量的。所以，DWR 无法转换 <CODE>contents</CODE><CODE> Map</CODE>。但是，对于购物车用户界面来说，用户需要查看的只是每个商品的名称和数量。所以我向 <CODE>Cart</CODE> 添加了一个名为 <CODE>getSimpleContents()</CODE> 的方法，它接受 <CODE>contents</CODE><CODE> Map</CODE> 并根据它构建一个简化的 <CODE>Map&lt;String,Integer&gt;</CODE>，只代表每个 <CODE>Item</CODE> 的名称和数量。这个用字符串作为键的 map 表示可以由 DWR 的转换器转换成 JavaScript。</P>
<P>客户对 <CODE>Cart</CODE> 感兴趣的其他字段是 <CODE>totalPrice</CODE>，它代表购物车中所有商品的金额汇总。使用 <CODE>Item</CODE>，我还提供了一个合成的成员叫作 <CODE>formattedTotalPrice</CODE>，它是金额汇总的格式化好的 <CODE>String</CODE> 表示。</P>
<P><A name=IDAXL0W><SPAN class=smalltitle><STRONG><FONT face=Arial>转换购物车</FONT></STRONG></SPAN></A></P>
<P>为了不让客户代码对 <CODE>Cart</CODE> 做两个调用（一个获得内容，一个获得总价），我想把这些数据一次全都发给客户。为了做到这一点，我添加了一个看起来有点儿怪的方法，如清单 5 所示：</P><BR><A name=listing5><B>清单 5. Cart.getCart() 方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/**
 * Returns the cart itself - for DWR
 * @return the cart
 */ 
public Cart getCart() {
  return this;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>虽然这个方法在普通的 Java 代码中可能完全是多余的（因为在调用这个方法时，已经有对 <CODE>Cart</CODE> 的引用），但它允许 DWR 客户让 <CODE>Cart</CODE> 把自己序列化成 JavaScript。</P>
<P>除了 <CODE>getCart()</CODE>，需要远程化的另一个方法是 <CODE>addItemToCart()</CODE>。这个方法接受目录 Item 的 ID 的 <CODE>String</CODE> 表示，把这个商品添加到 <CODE>Cart</CODE> 中并更新总价。方法还返回 <CODE>Cart</CODE>，这样客户代码在一个操作中就能更新 <CODE>Cart</CODE> 的内容并接收购物车的新状态。</P>
<P>清单 6 是扩展的 dwr.xml 配置文件，包含 <CODE>Cart</CODE> 类进行远程所需要的额外配置：</P><BR><A name=listing6><B>清单 6. 修改过的 dwr.xml 包含了 Cart 类</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
    "http://www.getahead.ltd.uk/dwr/dwr10.dtd"&gt;
&lt;dwr&gt;
  &lt;/allow&gt;
    &lt;/create creator="new" javascript="catalog"&gt;
      &lt;/param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/&gt;
      &lt;/include method="getItem"/&gt;
      &lt;/include method="findItems"/&gt;
    &lt;//create&gt;
    &lt;/convert converter="bean" 
      match="developerworks.ajax.store.Item"&gt;
      &lt;/param name="include" 
        value="id,name,description,formattedPrice"/&gt;
    &lt;//convert&gt;</FONT><SPAN class=boldcode>
<STRONG><FONT face="Lucida Console">    &lt;/create creator="new" scope="session" javascript="Cart"&gt;
      &lt;/param name="class" 
        value="developerworks.ajax.store.Cart"/&gt;
      &lt;/include method="addItemToCart"/&gt;
      &lt;/include method="getCart"/&gt;
    &lt;//create&gt;
    &lt;/convert converter="bean" 
      match="developerworks.ajax.store.Cart"&gt;
      &lt;/param name="include" 
        value="simpleContents,formattedTotalPrice"/&gt;
    &lt;//convert&gt;</FONT></STRONG></SPAN>
<FONT face="Lucida Console">  &lt;//allow&gt;
&lt;/dwr&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在这个版本的 <CODE>dwr.xml</CODE> 中，我添加了 <CODE>Cart</CODE> 的 <CODE>creator</CODE> 和 <CODE>convertor</CODE>。<CODE>create</CODE> 元素指定应当把 <CODE>addItemToCart()</CODE> 和 <CODE>getCart()</CODE> 方法远程化，而且重要的是，生成的 <CODE>Cart</CODE> 实例应当放在用户的会话中。所以，购物车的内容在用户的请求之间会保留。</P>
<P><CODE>Cart</CODE> 的 <CODE>convert</CODE> 元素是必需的，因为远程的 <CODE>Cart</CODE> 方法返回的是 <CODE>Cart</CODE> 本身。在这里我指定在 <CODE>Cart</CODE> 的序列化 JavaScript 形式中应当存在的成员是 <CODE>simpleContents</CODE> 这个图和 <CODE>formattedTotalPrice</CODE> 这个字符串。</P>
<P>如果对这觉得有点儿不明白，那么只要记住 <CODE>create</CODE> 元素指定的是 DWR 客户可以调用的 <CODE>Cart</CODE> 服务器端方法，而 <CODE>convert</CODE> 元素指定在 <CODE>Cart</CODE> 的 JavaScript 序列化形式中包含的成员。</P>
<P>现在可以实现调用 <CODE>Cart</CODE> 的远程方法的客户端代码了。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAWQ0W><SPAN class=atitle><FONT face=Arial size=4>调用远程的 Cart 方法</FONT></SPAN></A></P>
<P>首先，当商店的 Web 页首次装入时，我想检查保存在会话中的 <CODE>Cart</CODE> 的状态，看是否已经有一个购物车了。这是必需的，因为用户可能已经向 <CODE>Cart</CODE> 中添加了商品，然后刷新了页面或者导航到其他地方之后又返回来。在这些情况下，重新载入的页面需要用会话中的 <CODE>Cart</CODE> 数据对自己进行同步。我可以在页面的 onload 函数中用一个调用做到这一点，就像这样：<CODE>Cart.getCart(displayCart)</CODE>。请注意 <CODE>displayCart()</CODE> 是一个回调函数，由服务器返回的 <CODE>Cart</CODE> 响应数据调用。</P>
<P>如果 <CODE>Cart</CODE> 已经在会话中，那么<CODE>creator</CODE> 会检索它并调用它的 <CODE>getCart()</CODE> 方法。如果会话中没有 <CODE>Cart</CODE>，那么 <CODE>creator</CODE> 会实例化一个新的，把它放在会话中，并调用 <CODE>getCart()</CODE> 方法。</P>
<P>清单 7 显示了 <CODE>addToCartButtonHandler()</CODE> 函数的实现，当点击商品的 <B>Add to Cart</B> 按钮时会调用这个函数：</P><BR><A name=listing7><B>清单 7. addToCartButtonHandler() 实现</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/*
 * Handles a click on an Item's "Add to Cart" button
 */
function addToCartButtonHandler() {

  // 'this' is the button that was clicked.
  // Obtain the item ID that was set on it, and
  // add to the cart.
  Cart.addItemToCart(this.itemId,displayCart);
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>由 DWR 负责所有通信，所以客户上的添加到购物车行为就是一个函数。清单 8 显示了这个示例的最后一部分 —— <CODE>displayCart()</CODE> 回调的实现，它用 <CODE>Cart</CODE> 的状态更新用户界面：</P><BR><A name=listing8><B>清单 8. displayCart() 实现</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/*
 * Displays the contents of the user's shopping cart
 */
function displayCart(cart) {

  // Clear existing content of cart UI
  var contentsUL = $("contents");
  contentsUL.innerHTML="";

  // Loop over cart items
  for (var item in cart.simpleContents) {

    // Add a list element with the name and quantity of item
    var li = document.createElement("li");
    li.appendChild(document.createTextNode(
                    cart.simpleContents[item] + " x " + item
                  ));
    contentsUL.appendChild(li);
  }

  // Update cart total
  var totalSpan = $("totalprice");
  totalSpan.innerHTML = cart.formattedTotalPrice;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在这里重要的是要记住，<CODE>simpleContents</CODE> 是一个把 <CODE>String</CODE> 映射到数字的 JavaScript 数组。每个字符串都是一个商品的名称，关联数组中的对应数字就是购物车中该商品的数量。所以表达式 <CODE>cart.simpleContents[item] + " x " + item</CODE> 可能就会计算出 “<CODE>2 x Oolong 128MB CF Card</CODE>” 这样的结果。</P>
<P><A name=IDABU0W><SPAN class=smalltitle><STRONG><FONT face=Arial>DWR 商店应用程序</FONT></STRONG></SPAN></A></P>
<P>图 3 显示了这个基于 DWR 的 Ajax 应用程序的使用情况：显示了通过搜索检索到的商品，并在右侧显示用户的购物车：</P><BR><A name=figure3><B>图 3. 基于 DWR 的 Ajax 商店应用程序的使用情况</B></A><BR><IMG height=241 alt=示例场景的截屏，带有搜索结果和购物车 src="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/dwr-fig3.jpg" width=572><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDASU0W><SPAN class=atitle><FONT face=Arial size=4>DWR 的利弊</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><FONT face=Arial size=4><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></FONT></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=call-batching><B>调用批处理</B></A><BR>
<P>在 DWR 中，可以在一个 HTTP 请求中向服务器发送多个远程调用。调用 <CODE>DWREngine.beginBatch()</CODE> 告诉 DWR 不要直接分派后续的远程调用，而是把它们组合到一个批请求中。<CODE>DWREngine.endBatch()</CODE> 调用则把批请求发送到服务器。远程调用在服务器端顺序执行，然后调用每个 JavaScript 回调。</P>
<P>批处理在两方面有助于降低延迟：第一，避免了为每个调用创建 <CODE>XMLHttpRequest</CODE> 对象并建立相关的 HTTP 连接的开销。第二，在生产环境中，Web 服务器不必处理过多的并发 HTTP 请求，改进了响应时间。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P>现在可以看出用 DWR 实现由 Java 支持的 Ajax 应用程序有多么容易了。虽然示例场景很简单，我实现用例的手段也尽可能少，但是不应因此而低估 DWR 引擎相对于自己设计 Ajax 应用程序可以节约的工作量。在前一篇文章中，我介绍了手工设计 Ajax 请求和响应、把 Java 对象图转化成 JSON 表示的全部步骤，在这篇文章中，DWR 替我做了所有这些工作。我只编写了不到 50 行 JavaScript 就实现了客户机，而在服务器端，我需要做的所有工作就是给常规的 JavaBean 加上一些额外方法。</P>
<P>当然，每种技术都有它的不足。同任何 RPC 机制一样，在 DWR 中，可能很容易忘记对于远程对象进行的每个调用都要比本地函数调用昂贵得多。DWR 在隐藏 Ajax 的机械性方面做得很好，但是重要的是要记住网络并不是透明的 —— 进行 DWR 调用会有延迟，所以应用程序的架构应当让远程方法的粒度比较粗。正是为了这个目的，<CODE>addItemToCart()</CODE> 才返回 <CODE>Cart</CODE> 本身。虽然让 <CODE>addItemToCart()</CODE> 作为一个 void 方法可能更自然，但是这样的话对它的每个 DWR 调用后面都必须跟着一个 <CODE>getCart()</CODE> 调用以检索修改后的 <CODE>Cart</CODE> 状态。</P>
<P>对于延迟，DWR 在调用的批处理中有自己的解决方案（请参阅侧栏的 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#call-batching"><FONT color=#996699>调用批处理</FONT></A>）。如果不能为应用程序提供适当粗粒度的 Ajax 接口，那么只要有可能把多个远程调用组合到一个 HTTP 请求中，就请使用调用批处理。</P>
<P><A name=IDALW0W><SPAN class=smalltitle><STRONG><FONT face=Arial>分离的问题</FONT></STRONG></SPAN></A></P>
<P>从实质上看，DWR 在客户端和服务器端代码间形成了紧密的耦合，这有许多含义：首先，远程方法 API 的变化需要在 DWR 存根调用的 JavaScript 上反映出来。第二（也是最明显的），这种耦合会造成对客户端的考虑会渗入服务器端代码。例如，因为不是所有 Java 类型都能转化成 JavaScript，所以有时有必要给 Java 对象添加额外方法，好让它能够更容易地远程化。在示例场景中，我通过把 <CODE>getSimpleContents()</CODE> 方法添加到 <CODE>Cart</CODE> 来解决这个问题。我还添加了 <CODE>getCart()</CODE> 方法，它在 DWR 场景中是有用的，但在其他场景中则完全是多余的。由于远程对象粗粒度 API 的需要以及把某些 Java 类型转化成 JavaScript 的问题，所以可以看到远程 JavaBean 会被那些只对 Ajax 客户有用的方法“污染”。</P>
<P>为了克服这个问题，可以使用包装器类把额外的特定于 DWR 的方法添加到普通 JavaBean。这意味着 JavaBean 类的 Java 客户可能看不到与远程相关联的额外的毛病，而且也允许给远程方法提供更友好的名称 —— 例如用 <CODE>getPrice()</CODE> 代替 <CODE>getFormattedPrice()</CODE>。图 4 显示的 <CODE>RemoteCart</CODE> 类对 <CODE>Cart</CODE> 进行了包装，添加了额外的 DWR 功能：</P><BR><A name=figure4><B>图 4. RemoteCart 为远程功能对 Cart 做了包装</B></A><BR><IMG height=174 alt="RemoteCart 包装器类的类图" src="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/dwr-fig4.gif" width=550><BR>
<P>最后，需要记住：DWR Ajax 调用是异步的，所以不要期望它们会按照分派的顺序返回。在示例代码中我忽略了这个小问题，但是在这个系列的第一篇文章中，我演示了如何为响应加时间戳，以此作为保证数据到达顺序的一种简单手段。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDA2X0W><SPAN class=atitle><FONT face=Arial size=4>结束语</FONT></SPAN></A></P>
<P>正如所看到的，DWR 提供了许多东西 —— 它允许迅速而简单地创建到服务器端域对象的 Ajax 接口，而不需要编写任何 servlet 代码、对象序列化代码或客户端 <CODE>XMLHttpRequest</CODE> 代码。使用 DWR 部署到 Web 应用程序极为简单，而且 DWR 的安全性特性可以与 J2EE 基于角色的验证系统集成。但是 DWR 并不是对于任何一种应用程序架构都适合，所以在设计域对象的 API 时需要做些考虑。</P>
<P>如果想学习用 DWR 进行 Ajax 的利弊的更多内容，最好的方式就是下载并开始实践。DWR 有许多我没有介绍的特性， <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#download"><FONT color=#996699>文章源代码</FONT></A> 是把 DWR 投入使用的一个良好起点。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#resources"><FONT color=#996699>参考资料</FONT></A>，学习关于 Ajax、DWR 和相关技术的更多内容。</P>
<P>这个系列中要指出的最重要的一点是：对于 Ajax 应用程序，没有包治百病的解决方案。Ajax 是一个快速发展的领域，不断有新技术涌现。在这个系列的三篇文章中，我的重点在于带您开始在 Ajax 应用程序的 Web 层中利用 Java 技术 —— 不管是选择基于 <CODE>XMLHttpRequest</CODE> 的带有对象序列化框架的技术，还是选择 DWR 这样的更高级抽象。请在后续几个月中留意面向 Java 开发人员介绍 Ajax 的文章。</P><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><SPAN class=atitle><A name=download><FONT face=Arial size=4>下载</FONT></A></SPAN></P>
<TABLE class=data-table-1 cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TH>描述</TH>
<TH>名字</TH>
<TH style="TEXT-ALIGN: right">大小</TH>
<TH>&nbsp;下载方法</TH></TR>
<TR>
<TD class=tb-row>DWR source code</TD>
<TD noWrap>j-ajax3dwr.zip</TD>
<TD style="TEXT-ALIGN: right" noWrap>301 KB</TD>
<TD noWrap>&nbsp;<A class=fbox href="ftp://www6.software.ibm.com/software/developer/library/j-ajax3dwr.zip"><B><FONT color=#5c81a7>FTP</FONT></B></A></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD colSpan=5><FONT color=#5c81a7><IMG height=12 alt="" src="http://www.ibm.com/i/c.gif" width=12 border=0></FONT></TD></TR>
<TR>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www-128.ibm.com/developerworks/cn/whichmethod.html"><FONT color=#5c81a7>关于下载方法的信息</FONT></A></TD>
<TD><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=50></FONT></TD>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/sout.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www.adobe.com/products/acrobat/readstep2.html"><FONT color=#996699>获取 Adobe® Reader®</FONT></A></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT color=#996699><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT color=#996699><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><FONT face=Arial size=4>参考资料 </FONT></SPAN></A></P><B>学习</B><BR>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www-128.ibm.com/developerworks/java/library/j-ajax3/" target=_blank><FONT color=#5c81a7>英文原文</FONT></A>。<BR><BR>
<LI>“<A href="http://www.ibm.com/developerworks/webservices/library/ws-wsajax/"><FONT color=#5c81a7>Call SOAP Web services with AJAX, Part 1: Build the Web services client</FONT></A>” （James Snell，developerWorks，2005 年 10 月）：用 Ajax 实现 SOAP Web 服务客户机。<BR><BR>
<LI>“<A href="http://www.ibm.com/developerworks/cn/java/j-ajax2/"><FONT color=#996699>面向 Java 开发人员的 Ajax: Ajax 的 Java 对象序列化 </FONT></A>” （Philip McCarthy，developerWorks，2005 年 10 月）：学习在 Ajax 应用程序中进行数据序列化的技术。<BR><BR>
<LI>“<A href="http://www.ibm.com/developerworks/cn/java/j-ajax1/"><FONT color=#996699>面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序</FONT></A>” （Philip McCarthy，developerWorks，2005 年 9 月）：使用 Ajax 的第一步。<BR><BR>
<LI><A href="http://getahead.ltd.uk/dwr/documentation"><FONT color=#5c81a7>DWR Handbook</FONT></A> ：对 Ajax 和 DWR 的介绍。<BR><BR>
<LI>“<A href="http://today.java.net/pub/a/today/2005/08/25/dwr.html"><FONT color=#5c81a7>Developing AJAX Applications the Easy Way</FONT></A>” （Java.net，2005 年 8 月）：DWR 的创造者 Joe Walker 介绍如何编写简单的 Ajax 聊天服务器。<BR><BR>
<LI>请参阅教程 <A href="http://www.ibm.com/developerworks/edu/os-dw-os-phpajax-i.html"><FONT color=#5c81a7>An alternative to generating Ajax code with Java is using the Simple Ajax Toolkit (Sajax)</FONT></A>，学习如何从 Sajax 起步。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/cn/java/"><FONT color=#5c81a7>Java 技术专区</FONT></A> ：数百篇关于 Java 编程各方面的文章。</LI></UL><BR><B>获得产品和技术</B><BR>
<UL>
<LI><A href="http://getahead.ltd.uk/dwr/download"><FONT color=#5c81a7>下载 DWR</FONT></A> ：得到最新的 DWR jar 发行包。<BR><BR>
<LI><A href="http://oss.metaparadigm.com/jsonrpc/"><FONT color=#5c81a7>JSON-RPC-Java</FONT></A> ：用于 Java 的另一个 Ajax-RPC 引擎。<BR><BR></LI></UL><BR><B>讨论</B><BR>
<UL>
<LI>加入本文的<A href="javascript:void forumWindow()"><FONT color=#5c81a7>论坛</FONT></A> 。(您也可以通过点击文章顶部或者底部的论坛链接参加讨论。)<BR><BR>
<LI><A href="https://dwr.dev.java.net/"><FONT color=#5c81a7>DWR's Java.net pages</FONT></A> ：订阅 DWR 的邮件列表。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/blogs/"><FONT color=#5c81a7>developerWorks blogs</FONT></A> ：加入 developerWorks 社区。<BR><BR></LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax3/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><FONT face=Arial size=4>关于作者</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT face=Arial size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><FONT face=Arial size=4></FONT></P></TD>
<TD>
<P>Philip McCarthy 是一位软件开发顾问，专攻 Java 和 Web 技术。他目前在位于 Bristol 的 HP 试验室从事 Hewlett Packard 数字媒体平台的工作 。在最近几年中，Phil 开发了多个采用异步服务器通信和 DOM 脚本的富 Web 客户端。他很高兴我们现在有了一个针对它们的名称。可以通过 Phil 的电子邮件 <A href="mailto:philmccarthy@gmail.com"><FONT color=#5c81a7>philmccarthy@gmail.com</FONT></A> 与他联系。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/ahwen/aggbug/31972.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ahwen/" target="_blank">ahwen</a> 2006-02-22 15:02 <a href="http://www.blogjava.net/ahwen/articles/31972.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>面向 Java 开发人员的 Ajax: Ajax 的 Java 对象序列化</title><link>http://www.blogjava.net/ahwen/articles/31971.html</link><dc:creator>ahwen</dc:creator><author>ahwen</author><pubDate>Wed, 22 Feb 2006 06:59:00 GMT</pubDate><guid>http://www.blogjava.net/ahwen/articles/31971.html</guid><wfw:comment>http://www.blogjava.net/ahwen/comments/31971.html</wfw:comment><comments>http://www.blogjava.net/ahwen/articles/31971.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ahwen/comments/commentRss/31971.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ahwen/services/trackbacks/31971.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1><SPAN style="COLOR: #999999">面向 Java 开发人员的 Ajax: </SPAN>Ajax 的 Java 对象序列化</H1>
<P id=subtitle>在 Ajax 应用程序中序列化数据的五种途径</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="&#13;&#10;&#9;如果您正在使用异步 JavaScript 和 XML（Ajax）进行 Java™ Web 开发，那么您最关心的问题可能就是把数据从服务器传递给客户机。在“面向 Java 开发人员的 Ajax”系列的第二篇文章中，Philip McCarthy 介绍了 Java 对象序列化的五种方式，并提供了选择最适合应用程序的数据格式和技术所需要的全部信息。" name=body><INPUT type=hidden value="面向 Java 开发人员的 Ajax: Ajax 的 Java 对象序列化" name=subject><INPUT type=hidden value=cn name=lang>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT color=#5c81a7>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img src="//www.ibm.com/i/c.gif" height="16" width="16" alt="" /></td><td width="122" class="small"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw_bold.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:void forumWindow()"><B><FONT color=#5c81a7>讨论</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#rate"><B><FONT color=#996699>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 中级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#author"><FONT color=#996699>Philip McCarthy </FONT></A>, 软件开发顾问, 独立顾问<BR></P>
<P>2005 年 10 月 24 日</P>
<BLOCKQUOTE>如果您正在使用异步 JavaScript 和 XML（Ajax）进行 Java™ Web 开发，那么您最关心的问题可能就是把数据从服务器传递给客户机。在 <I>面向 Java 开发人员的 Ajax</I> 系列的第二篇文章中，Philip McCarthy 介绍了 Java 对象序列化的五种方式，并提供了选择最适合应用程序的数据格式和技术所需要的全部信息。</BLOCKQUOTE>
<P>在这个系列的 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/"><FONT color=#996699>第一篇文章</FONT></A> 中，我介绍了 Ajax 的构造块：</P>
<UL>
<LI>如何用 JavaScript <CODE>XMLHttpRequest</CODE> 对象从 Web 页面向服务器发送异步请求。 
<LI>如何用 Java servlet 处理和响应请求（向客户机返回 XML 文档）。 
<LI>如何在客户端用响应文档更新页面视图。</LI></UL>
<P>这一次，我将继续讨论 Ajax 开发的基础知识，但是将侧重于许多 Java Web 开发人员最关心的问题：为客户机生成数据。</P>
<P>多数 Java 开发人员已经把模型-视图-控制器（MVC）模式应用在他们的 Web 应用程序上。在传统的 Web 应用程序中，视图组件由 JSP 或者其他表示技术（例如 Velocity 模板）构成。这些表示组件动态地生成全新的 HTML 页面，替代用户以前正在查看的页面，从而更新用户界面。但是，在 Java Web 应用程序使用 Ajax UI 的情况下，基于从 <CODE>XMLHttpRequest</CODE> 的响应接收到的数据，JavaScript 客户端代码对于更新用户看到的内容负有最终责任。从服务器的角度来看，视图成为它响应客户机请求而发送的数据表示。</P>
<P>这篇文章侧重于可以用来生成 Java 对象以数据为中心的视图的技术。我将演示可以把 JavaBeans 变成 XML 文档的各种方法，并且讨论每种方法的优劣。您将看到为什么 XML 并不总是最好的途径：对于简单的 Ajax 请求来说，传输纯文本更好。最后，我将介绍 JavaScript 对象标注（JSON）。JSON 允许数据以序列化的 JavaScript 对象图的形式传输，在客户端代码中处理序列化的 JavaScript 对象图极为容易。</P>
<P><A name=IDADDKTB><SPAN class=smalltitle><STRONG><FONT face=Arial>关于示例</FONT></STRONG></SPAN></A></P>
<P>我将使用一个示例应用程序和几个用例来演示这里讨论的技术特性和技术。图 1 显示的极为简单的数据模型可以表示示例用例。这个模型代表在线商店中的顾客帐户。顾客拥有以前订单的集合，每个订单包含几个商品。</P><BR><A name=figure1><B>图 1. 简单的对象模型</B></A><BR><IMG height=133 alt=代表顾客帐户的对象模型 src="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/ajaxobjects.jpg" width=541><BR>
<P>虽然 <CODE>XMLHttpRequest</CODE> 对于发送数据使用的格式没有做任何限制，但是对于多数目的来说，只发送传统的表单数据是适合的，所以我的讨论集中在服务器的响应上。响应也可以有基于文本的格式，但是正如它的名字表示的，<CODE>XMLHttpRequest</CODE> 具有内置的处理 XML 响应数据的能力。这使 XML 成为 Ajax 响应的默认选择，所以我们从 XML 格式开始讨论。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDA4DKTB><SPAN class=atitle><FONT face=Arial size=4>从 Java 类产生 XML</FONT></SPAN></A></P>
<P>把 Ajax 响应作为 XML 来传递有许多原因：每个支持 Ajax 的浏览器都有导航 XML 文档的方法，也有许多服务器端技术可以处理 XML 数据。通过制定一个方案，描述要交换的文档类型，在 Ajax 客户端和服务器端之间很容易定义合约，而且如果服务器端架构采用面向服务的方式，那么使用 XML 也可以允许非 Ajax 客户机使用您提供的数据。</P>
<P>我将考虑从 Java 对象产生 XML 数据的三种方法，并讨论每种方法的优劣。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAFEKTB><SPAN class=atitle><FONT face=Arial size=4>自行进行序列化</FONT></SPAN></A></P>
<P>首先，可以从对象图以编程的方式生成 XML。这种方式可以简单到只是在每个 JavaBean 类中实现 <CODE>toXml()</CODE> 方法即可。然后就可以选择合适的 XML API，让每个 bean 提供表示自己状态的元素，并递归地对自己的成员调用对象图。显然，这种方式无法扩展到大量的类，因为每个类都需要专门编写自己的 XML 生成代码。从好的方面来看，这是一个实现起来简单的方式，没有额外的配置支出或者更复杂的构建过程支出，任何 JavaBean 图都可以只用几个调用就变成 XML 文档。</P>
<P>在本系列 <A href="http://www.ibm.com/developerworks/cn/java/j-ajax1/"><FONT color=#996699>前一篇文章</FONT></A> 的示例代码中，我把 XML 标记字符串连接在一起，实现了 <CODE>toXml()</CODE> 方法。上次我就提到过，这是个糟糕的方法，因为它把确保标记配对、实体编码等工作的负担放在每个 <CODE>toXml()</CODE> 方法的代码中。在 Java 平台上有几个 XML API 可以替您做这些工作，这样您就可以把精力集中在 XML 的内容上。清单 1 用 JDOM API 实现了在线商店示例中表示订单的类中的 <CODE>toXml()</CODE>（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#figure1"><FONT color=#996699>图 1</FONT></A>）。</P><BR><A name=code1><B>清单 1. Order 类的 toXml() 的 JDOM 实现</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public Element toXml() {

  Element elOrder = new Element("order");
  elOrder.setAttribute("id",id);

  elOrder.setAttribute("cost",getFormattedCost());

  Element elDate = new Element("date").addContent(date);
  elOrder.addContent(elDate);

  Element elItems = new Element("items");
  for (Iterator&lt;Item&gt; iter = 
   items.iterator() ; iter.hasNext() ; ) {
    elItems.addContent(iter.next().toXml());
  }
  elOrder.addContent(elItems);

  return elOrder;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在这里可以看到用 JDOM 创建元素、使用属性和添加元素内容有多么简单。递归地调用复合 JavaBean 的 <CODE>toXml()</CODE> 方法是为了取得它们子图的 <CODE>Element</CODE> 表示。例如，<CODE>items</CODE> 元素的内容是通过调用 <CODE>Order</CODE> 聚合的每个 <CODE>Item</CODE> 对象上的 <CODE>toXml()</CODE> 得到的。</P>
<P>一旦所有的 JavaBean 都实现了 <CODE>toXml()</CODE> 方法，那么把任意对象图序列化成 XML 文档并返回给 Ajax 客户机就简单了，如清单 2 所示。</P><BR><A name=code2><B>清单 2. 从 JDOM 元素生成 XML 响应</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public void doGet(HttpServletRequest req, HttpServletResponse res)
  throws java.io.IOException, ServletException {

    String custId = req.getParameter("username");
    Customer customer = getCustomer(custId);

    Element responseElem = customer.toXml();
    Document responseDoc = new Document(responseElem);

    res.setContentType("application/xml");
    new XMLOutputter().output(responseDoc,res.getWriter());
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>JDOM 再次把工作变得非常简单。只需要在对象图返回的 XML 元素外面包装一个 <CODE>Document</CODE>，然后用 <CODE>XMLOutputter</CODE> 把文档写入 servlet 响应即可。清单 3 显示了用这种方式生成的 XML 示例，用 JDOM <CODE>Format.getPrettyFormat()</CODE> 对 <CODE>XMLOutputter</CODE> 进行初始化，格式化得非常好。在这个示例中，顾客只做了一个订单，包含两个商品。</P><BR><A name=code3><B>清单 3. 代表顾客的 XML 文档</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;customer username="jimmy66"&gt;
  &lt;realname&gt;James Hyrax&lt;/realname&gt;
  &lt;orders&gt;
    &lt;order id="o-11123" cost="$349.98"&gt;
      &lt;date&gt;08-26-2005&lt;/date&gt;
      &lt;items&gt;
        &lt;item id="i-55768"&gt;
          &lt;name&gt;Oolong 512MB CF Card&lt;/name&gt;
          &lt;description&gt;512 Megabyte Type 1 CompactFlash card. 
          Manufactured by Oolong Industries&lt;/description&gt;
          &lt;price&gt;$49.99&lt;/price&gt;
        &lt;/item&gt;
        &lt;item id="i-74491"&gt;
          &lt;name&gt;Fujak Superpix72 Camera&lt;/name&gt;
          &lt;description&gt;7.2 Megapixel digital camera featuring six 
          shooting modes and 3x optical zoom. Silver.&lt;/description&gt;
          &lt;price&gt;$299.99&lt;/price&gt;
        &lt;/item&gt;
      &lt;/items&gt;
    &lt;/order&gt;
  &lt;/orders&gt;
&lt;/customer&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=IDANHKTB><SPAN class=smalltitle><STRONG><FONT face=Arial>自行序列化的不足</FONT></STRONG></SPAN></A></P>
<P>有趣的是，清单 3 中的代码展示了让 JavaBean 把自己序列化为 XML 的一个主要不足。假设要用这个文档表示顾客的订单历史视图。在这种情况下，不太可能要显示每个历史订单中每个商品的完整说明，或者告诉顾客他或她自己的姓名。但是如果应用程序有一个 <CODE>ProductSearch</CODE> 类，它就是以 <CODE>Item</CODE> bean 列表的形式返回搜索结果，那么在 <CODE>Item</CODE> 的 XML 表示中包含说明可能会有帮助。而且，<CODE>Item</CODE> 类上代表当前库存水平的额外字段，在产品搜索视图中可能就是需要显示的有用信息。但是，不管当前的库存水平是否与当前情况相关（比如对顾客的订单历史来说），这个字段都会从包含 <CODE>Item</CODE> 的任何对象图中序列化出来。</P>
<P>从设计的角度来看，这是数据模型与视图生成耦合的经典问题。每个 bean 只能用一种途径序列化自己，一成不变的方式意味着 Ajax 交互最终要交换它们不需要交换的数据，因此造成客户端代码要从文档中找到需要的信息更加困难，而且也会增加带宽消耗和客户端的 XML 解析时间。这种耦合的另一个后果就是 XML 的语法不能脱离 Java 类独立变化。例如，对顾客文档的方案做修改，可能会影响多个 Java 类，造成它们也不得不做修改和重新编译。</P>
<P>我稍后会解决这些问题，但是首先来看一个对自行序列化方式的可伸缩性问题的解决方案：XML 绑定框架。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDANA0TB><SPAN class=atitle><FONT face=Arial size=4>XML 绑定框架</FONT></SPAN></A></P>
<P>近些年来，已经开发了多个 Java API 来简化 XML 文档到 Java 对象图的绑定过程。多数都提供了 XML 编排和拆解；也就是说，它们可以在 Java 对象图和 XML 之间执行双向会话。这些框架封装了 XML 处理的全部工作，这意味着应用程序代码只需要处理普通的 Java 类。它们还希望提供有用的辅助功能，例如文档验证。笼统来说，这些框架采用了两种不同的方式：代码生成和对象到 XML 映射。我将分别解释这两种方式。</P>
<P><A name=IDATA0TB><SPAN class=smalltitle><STRONG><FONT face=Arial>代码生成方式</FONT></STRONG></SPAN></A></P>
<P>使用代码生成的框架包括 XMLBeans、JAXB、Zeus 和 JBind。Castor 也能使用这项技术。这类框架的起点是描述文档数据类型的 XML 方案。使用框架提供的工具，就可以生成代表这些方案定义类型的 Java 类。最后，用这些生成的类编写应用程序，表示自己的模型数据，并通过框架提供的一些辅助机制把数据序列化成 XML。</P>
<P>如果应用程序要使用大型 XML 语法，那么代码生成方式是个很好的方法。在数十个类上编写定制 XML 序列化代码的可伸缩性问题由此消除。另一方面，也不再需要定义自己的 JavaBean。框架生成的 Java 类通常非常符合 XML 的结构，所以对它们进行编码很难。而且，生成的类变成哑数据容器，因为一般不能向它们添加行为。一般来说，在应用程序代码中要做些妥协，才能很好地处理方案生成的类型。另一个缺陷是如果修改方案，会造成生成的类也要修改，所以也就会对围绕它们编写的代码带来相应的影响。</P>
<P>这种类型的 XML 绑定框架在数据拆解时最有用（例如，使用 XML 文档并把它们转化成 Java 对象）。除非拥有大型数据模型而且有可能从生成的类中获益，否则基于代码生成的框架对于 Ajax 应用程序来说可能有很大的杀伤力。</P>
<P><A name=IDA1A0TB><SPAN class=smalltitle><STRONG><FONT face=Arial>映射方式</FONT></STRONG></SPAN></A></P>
<P>采用映射方式的框架包括 Castor 和 Apache Commons Betwixt。映射通常是比代码生成更灵活和更轻量的解决方案。首先，可以像通常一样编写 JavaBean，包括任何行为以及任何自己喜欢的方便的方法。然后，在运行时，调用框架中基于内省的编排器，并根据对象成员的类型、名称和值生成 XML 文档。通过定义类的映射文件，可以覆盖默认的绑定策略，并就类在 XML 中的表示方式对编排器提出建议。</P>
<P>这种方法是在可伸缩性与灵活性之间的良好折中。可以按照自己喜欢的方式编写 Java 类，编排器负责处理 XML。虽然映射定义文件编写起来简单，可伸缩性也足够好，但是映射规则最多只能改变标准的绑定行为，而且在对象结构和它们的 XML 表示之间总要残留一些耦合。最终，可能不得不在 Java 表示或 XML 格式之间任选一个做些折中，才能让映射方法起作用。</P>
<P><A name=IDACB0TB><SPAN class=smalltitle><STRONG><FONT face=Arial>数据绑定总结</FONT></STRONG></SPAN></A></P>
<P>Dennis Sosnoski 就 XML 数据绑定 API 的主题，在代码生成和代码映射两个方面写了深入的文章。如果想进一步研究这个领域，我推荐他在 Castor 和代码生成框架方面的精彩文章（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#resources"><FONT color=#996699>参考资料</FONT></A> 中的链接）。</P>
<P>总之，代码生成方式损失了过多的灵活性和方便性，对于典型的 Ajax 应用程序用处不大。另一方面，基于映射的框架可能工作得很好，但是要恰到好处地调整它们的映射策略，以便从对象生成需要的 XML。</P>
<P>所有的 XML 绑定 API 都具有手工序列化技术的一个主要不足：模型和视图的耦合。被限制为一个类型一个 XML 表示，就意味着在网络上总要有冗余数据传输。更严重的问题是，在情况要求客户端代码使用专门视图时，客户端代码却无法得到它，所以可能要费力地处理给定对象图的一成不变的视图。</P>
<P>在传统的 Web 应用程序开发中，采用页面模板系统把视图生成与控制器逻辑和模型数据干净地分离。这种方法在 Ajax 场景中也会有帮助。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAQB0TB><SPAN class=atitle><FONT face=Arial size=4>页面模板系统</FONT></SPAN></A></P>
<P>任何通用目的的页面模板技术都可以用来生成 XML，从而使 Ajax 应用程序根据自己的数据模型生成任何 XML 响应文档。额外收获是：模板可以用简单的、表现力强的标记语言编写，而不是用一行行的 Java 代码编写。清单 5 是一个 JSP 页面，采用了 <CODE>Customer</CODE> bean 并表示出定制的 XML 视图，适合客户端代码生成订单历史组件。</P><BR><A name=code4><B>清单 4. 生成订单历史文档的 JSP</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;?xml version="1.0"?&gt;
&lt;%@ page contentType="application/xml" %&gt;
&lt;%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %&gt;
&lt;c:set var="cust" value="${requestScope.customer}"/&gt;

&lt;orderhistory username="${cust.username}"&gt;
&lt;c:forEach var="order" items="${cust.orders}"&gt;
  &lt;order id="${order.id}" cost="${order.formattedCost}"&gt;
    &lt;date&gt;${order.date}&lt;/date&gt;
    &lt;items&gt;
    &lt;c:forEach var="item" items="${order.items}"&gt;
      &lt;item id="${item.id}"&gt;
        &lt;name&gt;&lt;c:out value="${item.name}" escapeXml="true"/&gt;&lt;/name&gt;
        &lt;price&gt;${item.formattedPrice}&lt;/price&gt;
      &lt;/item&gt;
    &lt;/c:forEach&gt;
    &lt;/items&gt;
  &lt;/order&gt;
&lt;/c:forEach&gt;
&lt;/orderhistory&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>这个简洁的模板只输出订单历史视图需要的数据，不输出不相关的资料（例如商品说明）。创建产品搜索视图的定制 XML 应当同样简单，这个视图包含每个商品的完整说明和库存水平。</P>
<P><A name=IDADC0TB><SPAN class=smalltitle><STRONG><FONT face=Arial>模板的问题</FONT></STRONG></SPAN></A></P>
<P>另一方面，现在我需要为每个不同视图创建一个新 JSP，而不能仅仅把需要的对象图组织起来并序列化它。从设计的角度来说，许多人可能会有争议，认为这无论如何是件好事，因为这意味着正式地考虑服务器要生成的文档类型。而且，因为我现在要处理通用的模板环境，而不是特定于 XML 的 API，所以确保标记匹配、元素和属性的顺序正确以及 XML 实体（例如 <CODE>&lt;</CODE> 或 <CODE>&amp;</CODE>）正确转义就成了我的责任。JSP 的核心 <CODE>out</CODE> 标记使后面这项工作变得很容易，但是不是所有的模板技术都提供了这样的机制。最后，没有方便的途径可以在服务器端根据方案检验生成的 XML 文档的正确性，但这毕竟不是要在生产环境中做的事，可以方便地在开发期间处理它。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAWC0TB><SPAN class=atitle><FONT face=Arial size=4>不用 XML 的响应数据</FONT></SPAN></A></P>
<P>迄今为止，我介绍的所有技术都用 XML 文档的形式生成服务器响应。但是，XML 有一些问题。其中一个就是延迟。浏览器不能立即解析 XML 文档并生成 DOM 模型，所以这会降低某些 Ajax 组件需要的“迅捷”感，特别是在较慢的机器上解析大型文档的时候更是如此。“现场搜索”就是一个示例，在这种搜索中，当用户输入搜索术语时，就会从服务器提取搜索结果并显示给用户。对于现场搜索组件来说，迅速地响应输入是非常重要的，但是同时它还需要迅速而持续地解析服务器的响应。</P>
<P>延迟是一个重要的考虑因素，但是避免使用 XML 的最大原因是差劲的客户端 DOM API。清单 5 显示了使用跨浏览器兼容的方式通过 DOM 得到某个值的时候，通常不得不面对的困难。</P><BR><A name=code5><B>清单 5. 在 JavaScript 中导航 XML 响应文档</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">// Find name of first item in customer's last order
var orderHistoryDoc = req.responseXML;

var orders = orderHistoryDoc.getElementsByTagName("order");
var lastOrder = orders[orders.length - 1];

var firstItem = lastOrder.getElementsByTagName("item")[0];
var itemNameElement = firstItem.firstChild;

var itemNameText = itemNameElement.firstChild.data;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>当元素中间存在空白时，情况就变得更加复杂，因为每个元素的 <CODE>firstChild</CODE> 经常是个空白文本节点。现在有 JavaScript 库可以缓解处理 XML 文档的麻烦。这些库包括 Sarissa （请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#resources"><FONT color=#996699>参考资料</FONT></A>）和 Google-ajaXSLT，这两个库都把 XPath 功能添加到了大多数浏览器中。</P>
<P>但是，想想替代方案还是值得的。除了 <CODE>responseXML</CODE> 之外，<CODE>XMLHttpRequest</CODE> 对象还提供了名为 <CODE>responseText</CODE> 的属性，这个属性只是以字符串的方式提供服务器的响应体。</P>
<P><A name=IDA2D0TB><SPAN class=smalltitle><STRONG><FONT face=Arial>responseText 属性</FONT></STRONG></SPAN></A></P>
<P>当服务器需要向客户机发送非常简单的值时，<CODE>responseText</CODE> 特别方便，它可以避免 XML 导致的带宽支出和处理支出。例如，简单的 true/false 响应可以由服务器以纯文本方式返回，可以是逗号分隔的简单的名称或数字列表。但是，一般来说，最好不要在同一个应用程序中把 XML 响应和纯文本响应混合使用；保持单一数据格式可以让代码抽象和重用更加简单。</P>
<P><CODE>responseText</CODE> 与 XML 响应数据结合时也会有用。在只需要从响应文档中提取单一值的场景中，“欺骗性”地把 XML 当作文本字符串，而不把它当作结构化的文档对待，会更方便。例如，清单 6 显示了如何用正则表达式从顾客的订单历史中提取第一笔订单的日期。不过，这实际是种花招，一般不应当依赖 XML 文档的词汇表达。</P><BR><A name=code6><B>清单 6. 用正则表达式处理 XMLHttpRequest 的 responseText 对象</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">var orderHistoryText = req.responseText;
var matches = orderHistoryText.match(/&lt;date&gt;(.*?)&lt;\/date&gt;/);

var date = matches[1];
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在某些情况下，采用即时方式使用 <CODE>responseText</CODE> 会比较方便。但是，理想情况下，应当有种途径，可以用一种能够让 JavaScript 轻松导航、却没有 XML 处理支出的格式表示复杂的结构化数据。幸运的是，确实存在这样一种格式。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAZE0TB><SPAN class=atitle><FONT face=Arial size=4>JavaScript 对象标注</FONT></SPAN></A></P>
<P>实际上，JavaScript 对象的大部分都由联合数组、数字索引数组、字符串、数字或者这些类型的嵌套组合而成。因为所有类型都可以用 JavaScript 直接声明，所以可以在一条语句中静态地定义对象图。清单 7 使用 JSON 语法声明了一个对象，并演示了如何访问这个对象。大括号表示联合数组（即对象），它的键 -值组合由逗号分隔。方括号表示数字索引数组。</P><BR><A name=code7><B>清单 7. 用 JSON 在 JavaScript 中直接声明一个简单对象</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">var band = {
  name: "The Beatles",
  members: [
    {
      name: "John",
      instruments: ["Vocals","Guitar","Piano"]
    },
    {
      name: "Paul",
      instruments: ["Vocals","Bass","Piano","Guitar"]
    },
    {
      name: "George",
      instruments: ["Guitar","Vocals"]
    },
    {
      name: "Ringo",
      instruments: ["Drums","Vocals"]
    }
  ]
};

// Interrogate the band object
var musician = band.members[3];
alert( musician.name
        + " played " + musician.instruments[0] 
        + " with " + band.name );
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>既然 JSON 是一个有趣的语言特性，那么它对 Ajax 有什么意义呢？妙处在于可以用 JSON 在 Ajax 服务器响应中通过网络发送 JavaScript 对象图。这意味着在客户端可以避免使用笨拙的 DOM API 对 XML 进行导航 —— 只需要分析 JSON 响应，就会立即得到可以访问的 JavaScript 对象图。但是，首先需要把 JavaBean 变成 JSON。</P>
<P><A name=IDAHF0TB><SPAN class=smalltitle><STRONG><FONT face=Arial>从 Java 类产生 JSON</FONT></STRONG></SPAN></A></P>
<P>不同 XML 生成技术所具有的优缺点也适用于 JSON 的产生。而且可以证明，存在需要再次使用表示模板技术的情况。但是，使用 JSON 在理念上更接近于在应用层之间传递序列化的对象，而不是创建应用程序状态的视图。我将介绍如何用 <CODE>org.json</CODE> 这个 Java API 在 Java 类上创建 <CODE>toJSONObject()</CODE> 方法。然后，就可以把 <CODE>JSONObject</CODE> 简单地序列化成 JSON。清单 8 反映了 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#code1"><FONT color=#996699>清单 1</FONT></A> 讨论的 XML，显示了 <CODE>Order</CODE> 类的 <CODE>toJSONObject()</CODE> 实现。</P><BR><A name=code8><B>清单 8. Order 类的 toJSONObject() 方法实现</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public JSONObject toJSONObject() {

  JSONObject json = new JSONObject();
  json.put("id",id);
  json.put("cost",getFormattedCost());
  json.put("date",date);

  JSONArray jsonItems = new JSONArray();
  for (Iterator&lt;Item&gt; iter = 
   items.iterator() ; iter.hasNext() ; ) {
    jsonItems.put(iter.next().toJSONObject());
  }
  json.put("items",jsonItems);

  return json;
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>可以看到，<CODE>org.json</CODE> API 非常简单。 <CODE>JSONObject</CODE> 代表 JavaScript 对象（即联合数组），有不同的 <CODE>put()</CODE> 方法，方法接受的 <CODE>String</CODE> 键和值是原生类型、<CODE>String</CODE> 类型或其他 JSON 类型。<CODE>JSONArray</CODE> 代表索引数组，所以它的 <CODE>put()</CODE> 方法只接受一个值。请注意在清单 8 中，创建 <CODE>jsonItems</CODE> 数组，然后再用 <CODE>put()</CODE> 把它附加到 <CODE>json</CODE> 对象上；可以用另外一种方法做这项工作，就是对每个项目调用 <CODE>json.accumulate("items",iter.next().toJSONObject());</CODE>。<CODE>accumulate()</CODE> 方法与 <CODE>put()</CODE> 类似，区别在于它把值添加到按照键进行识别的索引数组。</P>
<P>清单 9 显示了如何序列化 <CODE>JSONObject</CODE> 并把它写入 servlet 响应。</P><BR><A name=code9><B>清单 9. 从 JSONObject 生成序列化的 JSON 响应</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public void doGet(HttpServletRequest req, HttpServletResponse res) 
  throws java.io.IOException, ServletException {

	String custId = req.getParameter("username");
	Customer customer = getCustomer(custId);

	res.setContentType("application/x-json");
	res.getWriter().print(customer.toJSONObject());
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>可以看到，它实际上什么也没有做。在这里隐式调用的 <CODE>JSONObject</CODE> 的 <CODE>toString()</CODE> 方法做了所有工作。请注意，<CODE>application/x-json</CODE> 内容类型还有一点不确定 —— 在编写这篇文章的时候，关于 JSON 应当属于什么 MIME 类型还没有定论。但是，目前 <CODE>application/x-json</CODE> 是合理的选择。清单 10 显示了这个 servlet 代码的示例响应。</P><BR><A name=code10><B>清单 10. Customer bean 的 JSON 表示</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">{
  "orders": [
    {
      "items": [
        {
          "price": "$49.99",
          "description": "512 Megabyte Type 1 CompactFlash card. 
                              Manufactured by Oolong Industries",
          "name": "Oolong 512MB CF Card",
          "id": "i-55768"
        },
        {
          "price": "$299.99",
          "description": "7.2 Megapixel digital camera featuring six 
            shooting modes and 3x optical zoom. Silver.",
          "name": "Fujak Superpix72 Camera",
          "id": "i-74491"
        }
      ],
      "date": "08-26-2005",
      "cost": "$349.98",
      "id": "o-11123"
    }
  ],
  "realname": "James Hyrax",
  "username": "jimmy66"
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=IDAMJ0TB><SPAN class=smalltitle><STRONG><FONT face=Arial>在客户端使用 JSON </FONT></STRONG></SPAN></A></P>
<P>处理的最后一步是把在客户端把 JSON 数据变成 JavaScript 对象。这可以通过对 <CODE>eval()</CODE> 的简单调用实现，这个函数可以即时地解释包含 JavaScript 表达式的字符串。清单 11 把 JSON 响应转变成 JavaScript 对象图，然后执行清单 5 的任务，从顾客的最后一次订单中得到第一个商品的名称。</P><BR><A name=code11><B>清单 11. 评估 JSON 响应</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">var jsonExpression = "(" + req.responseText + ")";
var customer = eval(jsonExpression);

// Find name of first item in customer's last order
var lastOrder = customer.orders[customer.orders.length-1];
var name = lastOrder.items[0].name;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>比较清单 11 和 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#code5"><FONT color=#996699>清单 5</FONT></A> 可以发现使用 JSON 的客户端的优势。如果在 Ajax 项目中要在客户端对许多复杂的服务器响应进行导航，那么 JSON 可能适合您的需要。JSON 和 <CODE>XMLHttpRequest</CODE> 结合还会让 Ajax 交互看起来更像 RPC 调用而不是 SOA 请求，这对应用程序的设计可能会有意义。在下一篇文章中，我要研究的框架，就是明确地为了让 JavaScript 代码对服务器端对象进行远程方法调用而设计的。</P>
<P><A name=IDAIK0TB><SPAN class=smalltitle><STRONG><FONT face=Arial>JSON 的不足</FONT></STRONG></SPAN></A></P>
<P>JSON 也有它的不足。使用这里介绍的 JSON 方式，就没有办法针对每个请求对对象的序列化进行裁剪，所以不需要的字段可能经常会在网络上发送。另外，添加 <CODE>toJSONObject()</CODE> 方法到每个 JavaBean，可伸缩性不太好，虽然用内省和标注编写一个通用的 JavaBean 到 JSON 的序列化器可能很简单。最后，如果服务器端代码是面向服务的，没有单独针对处理 Ajax 客户请求调整过，那么由于对 XML 一致的支持，XML 会是更好的选择。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDATK0TB><SPAN class=atitle><FONT face=Arial size=4>比较序列化技术</FONT></SPAN></A></P>
<P>现在已经看到了把 Java 状态传输到 Ajax 客户端的五种不同技术。我讨论了自行手工编码 XML 序列化、通过代码生成的 XML 绑定、通过映射机制的 XML 绑定、基于模板的 XML 生成以及手工编码到 JSON 的序列化。每种技术都有自己的优势和不足，分别适用于不同的应用程序架构。 </P>
<P>为了总结每种方式的优势与不足，表 1 从六个方面进行了粗略的评分：</P>
<DL>
<DT><B>可伸缩性</B> 
<DD>描述技术适应大量数据类型的容易程度。对于每个附加类型，编码和配置工作量是否会增长？ 
<DT><B>易于集成</B> 
<DD>评估把技术集成到项目的简单程度。是否需要更加复杂的构建过程？是否增加了部署的复杂性？ 
<DT><B>Java 类 API</B> 
<DD>描述以指定方式处理服务器端 Java 对象的容易程度。是可以编写普通的 bean，还是不得不处理笨拙的文档表示？ 
<DT><B>对输出的控制</B> 
<DD>描述对类的序列化表示控制的精确程度。 
<DT><B>视图灵活性</B> 
<DD>评估从同一组对象是否可以创建不同的、定制的数据序列化。 
<DT><B>客户端数据访问</B> 
<DD>描述 JavaScript 代码处理服务器响应数据的难易程度。</DD></DL>
<P>
<TABLE class=data-table-1 cellSpacing=0 cellPadding=0 width="100%" summary=数据生成技术的相对价值 border=0>
<CAPTION><STRONG><FONT color=#ffffff>表 1. 数据生成技术的相对价值</FONT></STRONG></CAPTION>
<TBODY>
<TR>
<TH><FONT color=#ffffff></FONT></TH>
<TH>自行编写 XML</TH>
<TH>通过代码生成的 XML 绑定</TH>
<TH>通过映射的 XML 绑定</TH>
<TH>页面模板 XML</TH>
<TH>手工编码的 JSON 序列化</TH></TR>
<TR>
<TD class=tb-row>可伸缩性</TD>
<TD>差</TD>
<TD>好</TD>
<TD>一般</TD>
<TD>一般</TD>
<TD>差</TD></TR>
<TR class=alt-row>
<TD class=tb-row>易于集成</TD>
<TD>好</TD>
<TD>差</TD>
<TD>一般</TD>
<TD>一般</TD>
<TD>好</TD></TR>
<TR>
<TD class=tb-row>Java 类 API</TD>
<TD>好</TD>
<TD>差</TD>
<TD>好</TD>
<TD>好</TD>
<TD>好</TD></TR>
<TR class=alt-row>
<TD class=tb-row>对输出的控制</TD>
<TD>好</TD>
<TD>好</TD>
<TD>一般</TD>
<TD>好</TD>
<TD>好</TD></TR>
<TR>
<TD class=tb-row>视图灵活性</TD>
<TD>差</TD>
<TD>差</TD>
<TD>差</TD>
<TD>好</TD>
<TD>差</TD></TR>
<TR class=alt-row>
<TD class=tb-row>客户端数据访问</TD>
<TD>差</TD>
<TD>差</TD>
<TD>差</TD>
<TD>一般</TD>
<TD>好</TD></TR></TBODY></TABLE></P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDARN0TB><SPAN class=atitle><FONT face=Arial size=4>结束语</FONT></SPAN></A></P>
<P>表 1 中的数据并不表明某项序列化技术比其他的技术好。毕竟，六种标准的相对重要性取决于项目的具体情况。例如，如果要处理数百种数据类型，这时想要的是可伸缩性，那么代码生成可能就是最好的选择。如果需要为同一数据模型生成多个不同视图，那么就应当使用页面模板。如果处理的是小规模项目，想降低需要编写的 JavaScript 代码数量，那么请考虑 JSON。</P>
<P>希望这篇文章为您提供了选择适合自己应用程序的序列化技术所需要的信息。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#resources"><FONT color=#996699>参考资料</FONT></A> 一节，学习关于这里讨论的技术的更多内容。您还应当继续关注这个系列的下一篇文章，在下一篇文章中，我将介绍如何用直接 Web 远程（DWR）编写 Java Ajax 应用程序。DWR 框架支持从 JavaScript 代码中直接调用 Java 类上的方法。换句话说，它替您负责数据序列化的工作，所以您可以在更高的抽象层次上使用 Ajax。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><FONT face=Arial size=4>参考资料 </FONT></SPAN></A></P><B>学习</B><BR>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/java/library/j-ajax2/" target=_blank><FONT color=#5c81a7>英文原文</FONT></A> 。 <BR><BR>
<LI>“<A href="http://www.ibm.com/developerworks/xml/library/x-databdopt/"><FONT color=#5c81a7>Data binding, Part 1: Code generation approaches -- JAXB and more</FONT></A>”（Dennis Sosnoski，developerWorks，2003 年 1 月）：对 XML 数据绑定技术的深入观察。<BR><BR>
<LI>“<A href="http://www-128.ibm.com/developerworks/cn/xml/x-bindcastor/"><FONT color=#5c81a7>XML 与 Java 技术: 用 Castor 进行数据绑定</FONT></A>”（Dennis Sosnoski，developerWorks，2003 年 4 月）：使用 Castor 的映射方式的入门教程。<BR><BR>
<LI>“<A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-ajax1/"><FONT color=#5c81a7>AJAX 及使用 E4X 编写 Web 服务脚本，第 1 部分</FONT></A>”（Paul Fremantle 和 Anthony Elder，developerWorks，2005 年 4 月）：对使用 E4X 的 Ajax 的介绍，E4X 是对 JavaScript 的扩展，可以简化 XML 脚本编写。<BR><BR>
<LI><A href="http://www.manning.com/books/crane/"><FONT color=#5c81a7>Ajax In Action（Dave Crane，Eric Pascarello，Darren James；Manning，2005 年 11 月）</FONT></A>：深入介绍了 Ajax 的 MVC 技术，也涉及了 JSON。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/views/opensource/projectnews.jsp"><FONT color=#5c81a7>developerWorks open source project news</FONT></A>：当前开源项目的清单，例如 PAJAJ（PHP 异步 Javascript 和 JSON）。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/cn/java/"><FONT color=#5c81a7>Java 技术专区</FONT></A>：数百篇关于 Java 编程各方面的文章。<BR><BR></LI></UL><BR><B>获得产品和技术</B><BR>
<UL>
<LI><A href="http://sourceforge.net/projects/sarissa"><FONT color=#5c81a7>Sarissa</FONT></A>：一个支持 XPath 的跨浏览器的 JavaScript 库。<BR><BR>
<LI><A href="http://jakarta.apache.org/commons/betwixt/"><FONT color=#5c81a7>Jakarta Commons Betwixt</FONT></A>：一个灵活的基于映射的 XML 绑定库。<BR><BR>
<LI><A href="http://www.json.org/"><FONT color=#5c81a7>JSON</FONT></A>：用于 Java 和其他语言的 JavaScript 对象标注 API。<BR><BR></LI></UL><BR><B>讨论</B><BR>
<UL>
<LI>加入本文的<A href="javascript:void forumWindow()"><FONT color=#5c81a7>论坛</FONT></A> 。(您也可以通过点击文章顶部或者底部的论坛链接参加讨论。)<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/blogs/"><FONT color=#5c81a7>developerWorks blogs</FONT></A>：加入 developerWorks 社区。</LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax2/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><FONT face=Arial size=4>关于作者</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT face=Arial size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><FONT face=Arial size=4></FONT></P></TD>
<TD>
<P>Philip McCarthy 是一位软件开发顾问，专攻 Java 和 Web 技术。他目前在位于 Bristol 的 HP 实验室从事 Hewlett Packard 数字媒体平台的工作 。在最近几年中，Phil 开发了多个采用异步服务器通信和 DOM 脚本的富 Web 客户端。他很高兴我们现在有了一个针对它们的名称。可以通过 Phil 的电子邮件 <A href="mailto:philmccarthy@gmail.com"><FONT color=#5c81a7>philmccarthy@gmail.com</FONT></A> 与他联系。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/ahwen/aggbug/31971.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ahwen/" target="_blank">ahwen</a> 2006-02-22 14:59 <a href="http://www.blogjava.net/ahwen/articles/31971.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序</title><link>http://www.blogjava.net/ahwen/articles/31963.html</link><dc:creator>ahwen</dc:creator><author>ahwen</author><pubDate>Wed, 22 Feb 2006 06:44:00 GMT</pubDate><guid>http://www.blogjava.net/ahwen/articles/31963.html</guid><wfw:comment>http://www.blogjava.net/ahwen/comments/31963.html</wfw:comment><comments>http://www.blogjava.net/ahwen/articles/31963.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ahwen/comments/commentRss/31963.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ahwen/services/trackbacks/31963.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<P id=subtitle>Ajax 为更好的 Web 应用程序铺平了道路</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www-128.ibm.com/developerworks/cn/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp><INPUT type=hidden value="在 Web 应用程序开发中，页面重载周期是最大的一个使用障碍，对于 Java 开发人员来说也是一个严峻的挑战。在这个系列中，作者 Philip McCarthy 介绍了一种创建动态应用程序体验的开创性方式。Ajax（异步 JavaScript 和 XML）是一种编程技术，它允许为基于 Java 的 Web 应用程序把 Java 技术、XML 和 JavaScript 组合起来，从而打破页面重载的范式。" name=body><INPUT type=hidden value="面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序" name=subject><INPUT type=hidden value=cn name=lang>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT color=#5c81a7>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img src="//www.ibm.com/i/c.gif" height="16" width="16" alt="" /></td><td width="122" class="small"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw_bold.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:void forumWindow()"><B><FONT color=#5c81a7>讨论</FONT></B></A></P></TD></TR>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/dn.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#download"><B><FONT color=#996699>样例代码</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#rate"><B><FONT color=#996699>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 中级</P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#author"><FONT color=#996699>Philip McCarthy </FONT></A>, 软件开发顾问, 独立咨询顾问<BR></P>
<P>2005 年 10 月 20 日</P>
<BLOCKQUOTE>在 Web 应用程序开发中，页面重载循环是最大的一个使用障碍，对于 Java™ 开发人员来说也是一个严峻的挑战。在这个系列中，作者 Philip McCarthy 介绍了一种创建动态应用程序体验的开创性方式。Ajax（异步 JavaScript 和 XML）是一种编程技术，它允许为基于 Java 的 Web 应用程序把 Java 技术、XML 和 JavaScript 组合起来，从而打破页面重载的范式。</BLOCKQUOTE>
<P>Ajax（即异步 JavaScript 和 XML）是一种 Web 应用程序开发的手段，它采用客户端脚本与 Web 服务器交换数据。所以，不必采用会中断交互的完整页面刷新，就可以动态地更新 Web 页面。使用 Ajax，可以创建更加丰富、更加动态的 Web 应用程序用户界面，其即时性与可用性甚至能够接近本机桌面应用程序。</P>
<P>Ajax 不是一项技术，而更像是一个 <I>模式</I> —— 一种识别和描述有用的设计技术的方式。Ajax 是新颖的，因为许多开发人员才刚刚开始知道它，但是所有实现 Ajax 应用程序的组件都已经存在若干年了。它目前受到重视是因为在 2004 和 2005 年出现了一些基于 Ajax 技术的非常棒的动态 Web UI，最著名的就是 Google 的 GMail 和 Maps 应用程序，以及照片共享站点 Flickr。这些用户界面具有足够的开创性，有些开发人员称之为“Web 2.0”，因此对 Ajax 应用程序的兴趣飞速上升。</P>
<P>在这个系列中，我将提供使用 Ajax 开发应用程序需要的全部工具 。在第一篇文章中，我将解释 Ajax 背后的概念，演示为基于 Java 的 Web 应用程序创建 Ajax 界面的基本步骤。我将使用代码示例演示让 Ajax 应用程序如此动态的服务器端 Java 代码和客户端 JavaScript。最后，我将指出 Ajax 方式的一些不足，以及在创建 Ajax 应用程序时应当考虑的一些更广的可用性和访问性问题。</P>
<P><A name=IDA1CAKB><SPAN class=atitle><FONT face=Arial size=4>更好的购物车</FONT></SPAN></A></P>
<P>可以用 Ajax 增强传统的 Web 应用程序，通过消除页面装入从而简化交互。为了演示这一点，我采用一个简单的购物车示例，在向里面添加项目时，它会动态更新。这项技术如果整合到在线商店，那么用户可以持续地浏览和向购物车中添加项目，而不必在每次点击之后都等候完整的页面更新。虽然这篇文章中的有些代码特定于购物车示例，但是演示的技术可以应用于任何 Ajax 应用程序。清单 1 显示了购物车示例使用的有关 HTML 代码，整篇文章中都会使用这个 HTML。</P><BR><A name="Listing 1"><B>清单1. 购物车示例的有关片断</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;!-- Table of products from store's catalog, one row per item --&gt;
&lt;th&gt;Name&lt;/th&gt; &lt;th&gt;Description&lt;/th&gt; &lt;th&gt;Price&lt;/th&gt; &lt;th&gt;&lt;/th&gt;
...
&lt;tr&gt;
  &lt;!-- Item details --&gt;
  &lt;td&gt;Hat&lt;/td&gt; &lt;td&gt;Stylish bowler hat&lt;/td&gt; &lt;td&gt;$19.99&lt;/td&gt;
  &lt;td&gt;
    &lt;!-- Click button to add item to cart via Ajax request --&gt;
    &lt;button onclick="addToCart('hat001')"&gt;Add to Cart&lt;/button&gt;
  &lt;/td&gt;
&lt;/tr&gt;
...

&lt;!-- Representation of shopping cart, updated asynchronously --&gt;
&lt;ul id="cart-contents"&gt;

  &lt;!-- List-items will be added here for each item in the cart --&gt;
  
&lt;/ul&gt;

&lt;!-- Total cost of items in cart displayed inside span element --&gt;
Total cost: &lt;span id="total"&gt;$0.00&lt;/span&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT face="Lucida Console"><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAJDAKB><SPAN class=atitle><FONT face=Arial size=4>Ajax 往返过程</FONT></SPAN></A></P>
<P>Ajax 交互开始于叫作 <CODE>XMLHttpRequest</CODE> 的 JavaScript 对象。顾名思义，它允许客户端脚本执行 HTTP 请求，并解析 XML 服务器响应。Ajax 往返过程的第一步是创建 <CODE>XMLHttpRequest</CODE> 的实例。在 <CODE>XMLHttpRequest</CODE> 对象上设置请求使用的 HTTP 方法（<CODE>GET</CODE> 或 <CODE>POST</CODE>）以及目标 URL。</P>
<P>现在，您还记得 Ajax 的第一个 <I>a</I> 是代表 <I>异步（asynchronous）</I> 吗？在发送 HTTP 请求时，不想让浏览器挂着等候服务器响应。相反，您想让浏览器继续对用户与页面的交互进行响应，并在服务器响应到达时再进行处理。为了实现这个要求，可以在 <CODE>XMLHttpRequest</CODE> 上注册一个回调函数，然后异步地分派 <CODE>XMLHttpRequest</CODE>。然后控制就会返回浏览器，当服务器响应到达时，会调用回调函数。</P>
<P>在 Java Web 服务器上，请求同其他 <CODE>HttpServletRequest</CODE> 一样到达。在解析了请求参数之后，servlet 调用必要的应用程序逻辑，把响应序列化成 XML，并把 XML 写入 <CODE>HttpServletResponse</CODE>。</P>
<P>回到客户端时，现在调用注册在 <CODE>XMLHttpRequest</CODE> 上的回调函数，处理服务器返回的 XML 文档。最后，根据服务器返回的数据，用 JavaScript 操纵页面的 HTML DOM，把用户界面更新。图 1 是 Ajax 往返过程的顺序图。</P><BR><A name=figure1><B>图 1. Ajax 往返过程</B></A><BR><IMG height=630 alt=" Ajax 往返过程的顺序图" src="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/ajax.gif" width=566><BR>
<P>现在您对 Ajax 往返过程有了一个高层面的认识。下面我将放大其中的每一步骤，进行更详细的观察。如果过程中迷了路，请回头看图 1 —— 由于 Ajax 方式的异步性质，所以顺序并非十分简单。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAQFAKB><SPAN class=atitle><FONT face=Arial size=4>分派 XMLHttpRequest</FONT></SPAN></A></P>
<P>我将从 Ajax 序列的起点开始：创建和分派来自浏览器的 <CODE>XMLHttpRequest</CODE>。不幸的是，不同的浏览器创建 <CODE>XMLHttpRequest</CODE> 的方法各不相同。清单 2 的 JavaScript 函数消除了这些依赖于浏览器的技巧，它可以检测当前浏览器要使用的正确方式，并返回一个可以使用的 <CODE>XMLHttpRequest</CODE>。最好是把它当作辅助代码：只要把它拷贝到 JavaScript 库，并在需要 <CODE>XMLHttpRequest</CODE> 的时候使用它就可以了。</P><BR><A name=listing2><B>清单 2. 创建跨浏览器的 XMLHttpRequest</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/*
 * Returns a new XMLHttpRequest object, or false if this browser
 * doesn't support it
 */
function newXMLHttpRequest() {

  var xmlreq = false;

  if (window.XMLHttpRequest) {

    // Create XMLHttpRequest object in non-Microsoft browsers
    xmlreq = new XMLHttpRequest();

  } else if (window.ActiveXObject) {

    // Create XMLHttpRequest via MS ActiveX
    try {
      // Try to create XMLHttpRequest in later versions
      // of Internet Explorer

      xmlreq = new ActiveXObject("Msxml2.XMLHTTP");

    } catch (e1) {

      // Failed to create required ActiveXObject

      try {
        // Try version supported by older versions
        // of Internet Explorer

        xmlreq = new ActiveXObject("Microsoft.XMLHTTP");

      } catch (e2) {

        // Unable to create an XMLHttpRequest with ActiveX
      }
    }
  }

  return xmlreq;
}
  </FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>稍后我将讨论处理那些不支持 <CODE>XMLHttpRequest</CODE> 的浏览器的技术。目前，示例假设清单 2 的 <CODE>newXMLHttpRequest</CODE> 函数总能返回 <CODE>XMLHttpRequest</CODE> 实例。</P>
<P>返回示例的购物车场景，我想要当用户在目录项目上点击 Add to Cart 时启动 Ajax 交互。名为 <CODE>addToCart()</CODE> 的 <CODE>onclick</CODE> 处理函数负责通过 Ajax 调用来更新购物车的状态（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#listing1"><FONT color=#996699>清单 1</FONT></A>）。正如清单 3 所示，<CODE>addToCart()</CODE> 需要做的第一件事是通过调用清单 2 的 <CODE>newXMLHttpRequest()</CODE> 函数得到 <CODE>XMLHttpRequest</CODE> 对象。接下来，它注册一个回调函数，用来接收服务器响应（我稍后再详细解释这一步；请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#listing6"><FONT color=#996699>清单 6</FONT></A>）。</P>
<P>因为请求会修改服务器上的状态，所以我将用 HTTP <CODE>POST</CODE> 做这个工作。通过 <CODE>POST</CODE> 发送数据要求三个步骤。第一，需要打开与要通信的服务器资源的 <CODE>POST</CODE> 连接 —— 在这个示例中，服务器资源是一个映射到 URL <CODE>cart.do</CODE> 的 servlet。然后，我在 <CODE>XMLHttpRequest</CODE> 上设置一个头，指明请求的内容是表单 编码的数据。最后，我用表单编码的数据作为请求体发送请求。</P>
<P>清单 3 把这些步骤放在了一起。</P><BR><A name=listing3><B>清单 3. 分派 Add to Cart XMLHttpRequest</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/*
 * Adds an item, identified by its product code, to the shopping cart
 * itemCode - product code of the item to add.
 */
function addToCart(itemCode) {

  // Obtain an XMLHttpRequest instance
  var req = newXMLHttpRequest();

  // Set the handler function to receive callback notifications
  // from the request object
  var handlerFunction = getReadyStateHandler(req, updateCart);
  req.onreadystatechange = handlerFunction;
  
  // Open an HTTP POST connection to the shopping cart servlet.
  // Third parameter specifies request is asynchronous.
  req.open("POST", "cart.do", true);

  // Specify that the body of the request contains form data
  req.setRequestHeader("Content-Type", 
                       "application/x-www-form-urlencoded");

  // Send form encoded data stating that I want to add the 
  // specified item to the cart.
  req.send("action=add&amp;item="+itemCode);
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>这就是建立 Ajax 往返过程的第一部分，即创建和分派来自客户机的 HTTP 请求。接下来是用来处理请求的 Java servlet 代码。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDA4AKKB><SPAN class=atitle><FONT face=Arial size=4>servlet 请求处理</FONT></SPAN></A></P>
<P>用 servlet 处理 <CODE>XMLHttpRequest</CODE>，与处理普通的浏览器 HTTP 请求一样。可以用 <CODE>HttpServletRequest.getParameter()</CODE> 得到在 POST 请求体中发送的表单编码数据。Ajax 请求被放进与来自应用程序的常规 Web 请求一样的 <CODE>HttpSession</CODE> 中。对于示例购物车场景来说，这很有用，因为这让我可以把购物车状态封装在 JavaBean 中，并在请求之间在会话中维持这个状态。</P>
<P>清单 4 是处理 Ajax 请求、更新购物车的简单 servlet 的一部分。<CODE>Cart</CODE> bean 是从用户会话中获得的，并根据请求参数更新它的状态。然后 <CODE>Cart</CODE> 被序列化成 XML，XML 又被写入 <CODE>ServletResponse</CODE>。重要的是把响应的内容类型设置为 <CODE>application/xml</CODE>，否则 <CODE>XMLHttpRequest</CODE> 不会把响应内容解析成 XML DOM。</P><BR><A name=listing4><B>清单 4. 处理 Ajax 请求的 servlet 代码</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public void doPost(HttpServletRequest req, HttpServletResponse res)
                        throws java.io.IOException {

  Cart cart = getCartFromSession(req);

  String action = req.getParameter("action");
  String item = req.getParameter("item");
  
  if ((action != null)&amp;&amp;(item != null)) {

    // Add or remove items from the Cart
    if ("add".equals(action)) {
      cart.addItem(item);

    } else if ("remove".equals(action)) {
      cart.removeItems(item);

    }
  }

  // Serialize the Cart's state to XML
  String cartXml = cart.toXml();

  // Write XML to response.
  res.setContentType("application/xml");
  res.getWriter().write(cartXml);
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>清单 5 显示了 <CODE>Cart.toXml()</CODE> 方法生成的示例 XML。它很简单。请注意 <CODE>cart</CODE> 元素的 <CODE>generated</CODE> 属性，它是 <CODE>System.currentTimeMillis()</CODE> 生成的一个时间戳。</P><BR><A name=listing5><B>清单 5. Cart 对象的XML 序列化示例 </B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;?xml version="1.0"?&gt;
&lt;cart generated="1123969988414" total="$171.95"&gt;
  &lt;item code="hat001"&gt;
    &lt;name&gt;Hat&lt;/name&gt;
    &lt;quantity&gt;2&lt;/quantity&gt;
  &lt;/item&gt;
  &lt;item code="cha001"&gt;
    &lt;name&gt;Chair&lt;/name&gt;
    &lt;quantity&gt;1&lt;/quantity&gt;
  &lt;/item&gt;
  &lt;item code="dog001"&gt;
    &lt;name&gt;Dog&lt;/name&gt;
    &lt;quantity&gt;1&lt;/quantity&gt;
  &lt;/item&gt;
&lt;/cart&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>如果查看应用程序源代码（可以从 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#download"><FONT color=#996699>下载</FONT></A> 一节得到）中的 Cart.java，可以看到生成 XML 的方式只是把字符串添加在一起。虽然对这个示例来说足够了，但是对于从 Java 代码生成 XML 来说则是最差的方式。我将在这个系列的下一期中介绍一些更好的方式。</P>
<P>现在您已经知道了 <CODE>CartServlet</CODE> 响应 <CODE>XMLHttpRequest</CODE> 的方式。下一件事就是返回客户端，查看如何用 XML 响应更新页面状态。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDAZDKKB><SPAN class=atitle><FONT face=Arial size=4>用 JavaScript 进行响应处理</FONT></SPAN></A></P>
<P><CODE>XMLHttpRequest</CODE> 的 <CODE>readyState</CODE> 属性是一个数值，它指出请求生命周期的状态。它从 0（代表“未初始化”）变化到 4（代表“完成”）。每次 <CODE>readyState</CODE> 变化时，<CODE>readystatechange</CODE> 事件就触发，由 <CODE>onreadystatechange</CODE> 属性指定的事件处理函数就被调用。</P>
<P>在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#listing3"><FONT color=#996699>清单 3</FONT></A> 中已经看到了如何调用 <CODE>getReadyStateHandler()</CODE> 函数创建事件处理函数。然后把这个事件处理函数分配给 <CODE>onreadystatechange</CODE> 属性。<CODE>getReadyStateHandler()</CODE> 利用了这样一个事实：函数是 JavaScript 中的一级对象。这意味着函数可以是其他函数的参数，也可以创建和返回其他函数。<CODE>getReadyStateHandler()</CODE> 的工作是返回一个函数，检查 <CODE>XMLHttpRequest</CODE> 是否已经完成，并把 XML 响应传递给调用者指定的事件处理函数。清单 6 是 <CODE>getReadyStateHandler()</CODE> 的代码。</P><BR><A name=listing6><B>清单 6. getReadyStateHandler() 函数</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">/*
 * Returns a function that waits for the specified XMLHttpRequest
 * to complete, then passes its XML response to the given handler function.
 * req - The XMLHttpRequest whose state is changing
 * responseXmlHandler - Function to pass the XML response to
 */
function getReadyStateHandler(req, responseXmlHandler) {

  // Return an anonymous function that listens to the 
  // XMLHttpRequest instance
  return function () {

    // If the request's status is "complete"
    if (req.readyState == 4) {
      
      // Check that a successful server response was received
      if (req.status == 200) {

        // Pass the XML payload of the response to the 
        // handler function
        responseXmlHandler(req.responseXML);

      } else {

        // An HTTP problem has occurred
        alert("HTTP error: "+req.status);
      }
    }
  }
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></FONT></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=IDA2FKKB><B>HTTP 状态码</B></A><BR>
<P>在清单 6 中，检查 <CODE>XMLHttpRequest</CODE> 的 <CODE>status</CODE> 属性以查看请求是否成功完成。<CODE>status</CODE> 包含服务器响应的 HTTP 状态码。在执行简单的 <CODE>GET</CODE> 和 <CODE>POST</CODE> 请求时，可以假设任何大于 200 （OK）的码都是错误。如果服务器发送重定向响应（例如 301 或 302），浏览器会透明地进行重定向并从新的位置获取资源；<CODE>XMLHttpRequest</CODE> 看不到重定向状态码。而且，浏览器会自动添加 <CODE>Cache-Control: no-cache</CODE> 头到所有 <CODE>XMLHttpRequest</CODE>，这样客户代码永远也不用处理 304（未经修改）服务器响应。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A name=IDACHKKB><SPAN class=smalltitle><STRONG><FONT face=Arial>关于 getReadyStateHandler()</FONT></STRONG></SPAN></A></P>
<P><CODE>getReadyStateHandler()</CODE> 是段相对复杂的代码，特别是如果您不习惯阅读 JavaScript 的话。但是通过把这个函数放在 JavaScript 库中，就可以处理 Ajax 服务器响应，而不必处理 <CODE>XMLHttpRequest</CODE> 的内部细节。重要的是要理解如何在自己的代码中使用 <CODE>getReadyStateHandler()</CODE>。</P>
<P>在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#listing3"><FONT color=#996699>清单 3</FONT></A> 中看到了 <CODE>getReadyStateHandler()</CODE> 像这样被调用：<CODE>handlerFunction = getReadyStateHandler(req, updateCart)</CODE>。在这个示例中，<CODE>getReadyStateHandler()</CODE> 返回的函数将检查在 <CODE>req</CODE> 变量中的 <CODE>XMLHttpRequest</CODE> 是否已经完成，然后用响应的 XML 调用名为 <CODE>updateCart</CODE> 的函数。</P>
<P><A name=IDAVIKKB><SPAN class=smalltitle><STRONG><FONT face=Arial>提取购物车数据</FONT></STRONG></SPAN></A></P>
<P>清单 7 是 <CODE>updateCart()</CODE> 本身的代码。函数用 DOM 调用检查购物车的 XML 文档，然后更新 Web 页面（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#listing1"><FONT color=#996699>清单 1</FONT></A>），反映新的购物车内容。这里的重点是用来从 XML DOM 提取数据的调用。<CODE>cart</CODE> 元素的 <CODE>generated</CODE> 属性是在 <CODE>Cart</CODE> 序列化为 XML 时生成的一个时间戳，检查它可以保证新的购物车数据不会被旧的数据覆盖。Ajax 请求天生是异步的，所以这个检查可以处理服务器响应未按次序到达的情况。</P><BR><A name=listing7><B>清单 7. 更新页面，反映购物车的 XML 文档</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">function updateCart(cartXML) {

 // Get the root "cart" element from the document
 var cart = cartXML.getElementsByTagName("cart")[0];

 // Check that a more recent cart document hasn't been processed
 // already
 var generated = cart.getAttribute("generated");
 if (generated &gt; lastCartUpdate) {
   lastCartUpdate = generated;

   // Clear the HTML list used to display the cart contents
   var contents = document.getElementById("cart-contents");
   contents.innerHTML = "";

   // Loop over the items in the cart
   var items = cart.getElementsByTagName("item");
   for (var I = 0 ; I &lt; items.length ; I++) {

     var item = items[I];

     // Extract the text nodes from the name and quantity elements
     var name = item.getElementsByTagName("name")[0]
                                               .firstChild.nodeValue;
                                               
     var quantity = item.getElementsByTagName("quantity")[0]
                                               .firstChild.nodeValue;

     // Create and add a list item HTML element for this cart item
     var li = document.createElement("li");
     li.appendChild(document.createTextNode(name+" x "+quantity));
     contents.appendChild(li);
   }
 }

 // Update the cart's total using the value from the cart document
 document.getElementById("total").innerHTML = 
                                          cart.getAttribute("total");
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>到此，整个 Ajax 往返过程完成了，但是您可能想让 Web 应用程序运行一下查看实际效果（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#download"><FONT color=#996699>下载</FONT></A> 一节）。这个示例非常简单，有很多需要改进之处。例如，我包含了从购物车中清除项目的服务器端代码，但是无法从 UI 访问它。作为一个好的练习，请试着在应用程序现有的 JavaScript 代码之上构建出能够实现这个功能的代码。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDA4JKKB><SPAN class=atitle><FONT face=Arial size=4>使用 Ajax 的挑战</FONT></SPAN></A></P>
<P>就像任何技术一样，使用 Ajax 也有许多出错的可能性。我目前在这里讨论的问题还缺乏容易的解决方案，但是会随着 Ajax 的成熟而改进。随着开发人员社区增加开发 Ajax 应用程序的经验，将会记录下最佳实践和指南。</P>
<P><A name=IDAEKKKB><SPAN class=smalltitle><STRONG><FONT face=Arial>XMLHttpRequest 的可用性</FONT></STRONG></SPAN></A></P>
<P>Ajax 开发人员面临的一个最大问题是：在没有 <CODE>XMLHttpRequest</CODE> 可用时该如何响应？虽然主要的现代浏览器都支持 <CODE>XMLHttpRequest</CODE>，但仍然有少数用户的浏览器不支持，或者浏览器的安全设置阻止使用 <CODE>XMLHttpRequest</CODE>。如果开发的 Web 应用程序要部署在企业内部网，那么可能拥有指定支持哪种浏览器的权力，从而可以认为 <CODE>XMLHttpRequest</CODE> 总能使用。但是，如果要部署在公共 Web 上，那么就必须当心，如果假设 <CODE>XMLHttpRequest</CODE> 可用，那么就可能会阻止那些使用旧的浏览器、残疾人专用浏览器和手持设备上的轻量级浏览器的用户使用您的应用程序。</P>
<P>所以，您应当努力让应用程序“平稳降级”，在没有 <CODE>XMLHttpRequest</CODE> 支持的浏览器中也能够工作。在购物车的示例中，把应用程序降级的最好方式可能是让 Add to Cart 按钮执行一个常规的表单提交，刷新页面来反映购物车更新后的状态。Ajax 的行为应当在页面装入的时候就通过 JavaScript 添加到页面，只有在 <CODE>XMLHttpRequest</CODE> 可用时才把 JavaScript 事件处理函数附加到每个 Add to Cart 按钮。另一种方式是在用户登录时检测 <CODE>XMLHttpRequest</CODE> 是否可用，然后相应地提供应用程序的 Ajax 版本或基于表单的普通版本。</P>
<P><A name=IDANLKKB><SPAN class=smalltitle><STRONG><FONT face=Arial>可用性考虑</FONT></STRONG></SPAN></A></P>
<P>关于 Ajax 应用程序的某些可用性问题比较普遍。例如，让用户知道他们的输入已经注册了可能是重要的，因为沙漏光标和 spinning 浏览器的常用反馈机制“throbber”对 <CODE>XMLHttpRequest</CODE> 不适用。一种技术是用“Now updating...”类型的信息替换 Submit 按钮，这样用户在等候响应期间就不会反复单击按钮了。</P>
<P>另一个问题是，用户可能没有注意到他们正在查看的页面的某一部分已经更新了。可以使用不同的可视技术，把用户的眼球带到页面的更新区域，从而缓解这个问题。由 Ajax 更新页面造成的其他问题还包括：“破坏了”浏览器的后退按钮，地址栏中的 URL 也无法反映页面的整个状态，妨碍了设置书签。请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#resources"><FONT color=#996699>参考资料</FONT></A> 一节，获得专门解决 Ajax 应用程序可用性问题的文章。</P>
<P><A name=IDA4LKKB><SPAN class=smalltitle><STRONG><FONT face=Arial>服务器负载</FONT></STRONG></SPAN></A></P>
<P>用 Ajax 实现代替普通的基于表单的 UI，会大大提高对服务器发出的请求数量。例如，一个普通的 Google Web 搜索对服务器只有一个请求，是在用户提交搜索表单时出现的。而 Google Suggest 试图自动完成搜索术语，它要在用户输入时向服务器发送多个请求。在开发 Ajax 应用程序时，要注意将要发送给服务器的请求数量以及由此造成的服务器负荷。降低服务器负载的办法是，在客户机上对请求进行缓冲并且缓存服务器响应（如果可能的话）。还应该尝试将 Ajax Web 应用程序设计为在客户机上执行尽可能多的逻辑，而不必联络服务器。</P>
<P><A name=IDAEMKKB><SPAN class=smalltitle><STRONG><FONT face=Arial>处理异步</FONT></STRONG></SPAN></A></P>
<P>非常重要的是，要理解无法保证 <CODE>XMLHttpRequest</CODE> 会按照分派它们的顺序完成。实际上，应当假设它们不会按顺序完成，并且在设计应用程序时把这一点记在心上。在购物车的示例中，使用最后更新的时间戳来确保新的购物车数据不会被旧的数据覆盖（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#listing7"><FONT color=#996699>清单 7</FONT></A>）。这个非常基本的方式可以用于购物车场景，但是可能不适合其他场景。所以在设计时请考虑如何处理异步的服务器响应。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=IDATMKKB><SPAN class=atitle><FONT face=Arial size=4>结束语</FONT></SPAN></A></P>
<P>现在您对 Ajax 的基本原则应当有了很好的理解，对参与 Ajax 交互的客户端和服务器端组件也应当有了初步的知识。这些是基于 Java 的 Ajax Web 应用程序的构造块。另外，您应当理解了伴随 Ajax 方式的一些高级设计问题。创建成功的 Ajax 应用程序要求整体考虑，从 UI 设计到 JavaScript 设计，再到服务器端架构；但是您现在应当已经武装了考虑其他这些方面所需要的核心 Ajax 知识。</P>
<P>如果使用这里演示的技术编写大型 Ajax 应用程序的复杂性让您觉得恐慌，那么有好消息给您。由于 Struts、Spring 和 Hibernate 这类框架的发展把 Web 应用程序开发从底层 Servlet API 和 JDBC 的细节中抽象出来，所以正在出现简化 Ajax 开发的工具包。其中有些只侧重于客户端，提供了向页面添加可视效果的简便方式，或者简化了对 <CODE>XMLHttpRequest</CODE> 的使用。有些则走得更远，提供了从服务器端代码自动生成 Ajax 接口的方式。这些框架替您完成了繁重的任务，所以您可以采用更高级的方式进行 Ajax 开发。我在这个系列中将研究其中的一些。</P>
<P>Ajax 社区正在快速前进，所以会有大量有价值的信息涌现。在阅读这个系列的下一期之前，我建议您参考 <A href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#resources"><FONT color=#996699>参考资料</FONT></A> 一节中列出的文章，特别是如果您是刚接触 Ajax 或客户端开发的话。您还应当花些时间研究示例源代码并考虑一些增强它的方式。</P>
<P>在这个系列的下一篇文章中，我将深入讨论 <CODE>XMLHttpRequest</CODE> API，并推荐一些从 JavaBean 方便地创建 XML 的方式。我还将介绍替代 XML 进行 Ajax 数据传递的方式，例如 JSON（JavaScript Object Notation）轻量级数据交换格式。</P><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><SPAN class=atitle><A name=download><FONT face=Arial size=4>下载</FONT></A></SPAN></P>
<TABLE class=data-table-1 cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TH>描述</TH>
<TH>名字</TH>
<TH style="TEXT-ALIGN: right">大小</TH>
<TH>&nbsp;下载方法</TH></TR>
<TR>
<TD class=tb-row>Sample code</TD>
<TD noWrap>j-ajax1.zip</TD>
<TD style="TEXT-ALIGN: right" noWrap>8 KB</TD>
<TD noWrap>&nbsp;<A class=fbox href="ftp://www6.software.ibm.com/software/developer/library/j-ajax1.zip"><B><FONT color=#5c81a7>FTP</FONT></B></A></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD colSpan=5><FONT color=#5c81a7><IMG height=12 alt="" src="http://www.ibm.com/i/c.gif" width=12 border=0></FONT></TD></TR>
<TR>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www-128.ibm.com/developerworks/cn/whichmethod.html"><FONT color=#5c81a7>关于下载方法的信息</FONT></A></TD>
<TD><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=50></FONT></TD>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/sout.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www.adobe.com/products/acrobat/readstep2.html"><FONT color=#996699>获取 Adobe® Reader®</FONT></A></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT color=#996699><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT color=#996699><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><FONT face=Arial size=4>参考资料 </FONT></SPAN></A></P><B>学习</B><BR>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/java/library/j-ajax1/" target=_blank><FONT color=#5c81a7>英文原文</FONT></A>。<BR><BR>
<LI>“<A href="http://www.ibm.com/developerworks/xml/library/x-matters41.html"><FONT color=#5c81a7>Beyond the DOM</FONT></A>”（Dethe Elza， developerWorks，2005 年 5 月）：进行 XML 文档访问的有用的 JavaScript 技术。<BR><BR>
<LI>“<A href="http://www-128.ibm.com/developerworks/cn/webservices/ws-ajax1/"><FONT color=#5c81a7>AJAX 及使用 E4X 编写 Web 服务脚本，第 1 部分</FONT></A>”（Paul Fremantle 和 Anthony Elder，developerWorks，2005 年 4 月）：用 Ajax 在支持 E4X JavaScript 扩展的浏览器中进行 SOAP 调用。<BR><BR>
<LI>“<A href="http://www.adaptivepath.com/publications/essays/archives/000385.php"><FONT color=#5c81a7>Ajax: A New Approach to Web Applications</FONT></A>”（Jesse James Garrett，Adaptive Path，2005 年 2 月）：介绍 Ajax 起源的短文。<BR><BR>
<LI><A href="https://bpcatalog.dev.java.net/nonav/ajax/index.html"><FONT color=#5c81a7>The Java BluePrints Solutions Catalog</FONT></A>：介绍了 Ajax 在几个常见 Web 应用程序场景中的应用。<BR><BR>
<LI><A href="http://ajaxpatterns.org/#Functionality_and_Usability_Patterns_.2832.29"><FONT color=#5c81a7>AjaxPatterns.org</FONT></A>：包含多项改进 Ajax 应用程序的 UI 技术。<BR><BR>
<LI><A href="http://www.baekdal.com/articles/Usability/XMLHttpRequest-guidelines/"><FONT color=#5c81a7>XMLHttpRequest Usability Guidelines</FONT></A>：对使用 Ajax 提高用户体验的建议。<BR><BR>
<LI><A href="http://alexbosworth.backpackit.com/pub/67688"><FONT color=#5c81a7>Ajax Mistakes</FONT></A>：Ajax 应用程序应当避免的可用性问题。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/cn/java/"><FONT color=#5c81a7>Java 技术专区</FONT></A>：在这里可以找到关于 Java 编程的各个方面的文章。<BR><BR></LI></UL><BR><B>获得产品和技术</B><BR>
<UL>
<LI><A href="http://www.mozilla.org/products/firefox/"><FONT color=#5c81a7>Mozilla Firefox</FONT></A>：DOM Inspector 和 JavaScript Debugger 扩展消除了许多 Ajax 开发的痛苦。 <BR><BR></LI></UL><BR><B>讨论</B><BR>
<UL>
<LI>加入本文的<A href="javascript:void forumWindow()"><FONT color=#5c81a7>论坛</FONT></A> 。(您也可以通过点击文章顶部或者底部的论坛链接参加讨论。)<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/blogs/"><FONT color=#5c81a7>developerWorks blogs</FONT></A>：加入 developerWorks 社区。</LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-ajax1/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><FONT face=Arial size=4>关于作者</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT face=Arial size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><FONT face=Arial size=4></FONT></P></TD>
<TD>
<P>Philip McCarthy 是一位软件开发顾问，专攻 Java 和 Web 技术。他目前在位于 Bristol 的 HP 试验室从事 Hewlett Packard 数字媒体平台的工作。在最近几年中，Phil 开发了多个采用异步服务器通信和 DOM 脚本的富 Web 客户端。他很高兴我们现在有了一个针对它们的名称。可以通过 Phil 的电子邮件 <A href="mailto:philmccarthy@gmail.com"><FONT color=#5c81a7>philmccarthy@gmail.com</FONT></A> 与他联系。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/ahwen/aggbug/31963.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ahwen/" target="_blank">ahwen</a> 2006-02-22 14:44 <a href="http://www.blogjava.net/ahwen/articles/31963.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>