2005 年,伴随着 Web2.0 的东风, Ajax 逐渐进入国内开发人员的视线,并风靡全国。这个在 2005 年 2 月份由 Adaptive Path 公司的 Jesse James Garrett 命名的综合技术,综合利用 Javascript 、 XHTML 和 CSS 、 DOM 、 XMLHttpRequest (以下称之为 XHR )、 XML 和 XSTL 等技术,为 Web 应用程序提供异步的 http 请求处理方式,帮助 Web 应用程序实现类似桌面应用程序的快速反应功能并提供更加优秀的用户体验效果。
从 Ajax 身上,可以很清晰的看到各种技术整合到一起之后所表现出来的非凡魅力:
-
其使用 XHTML 和 CSS 实现标准化的呈现界面。
-
其使用 DOM 实现动态的显示和交互。
-
其使用 XHR 实现与服务器的异步通信。
-
其使用 Javascript 将 XHTML 、 DOM 、 XML 、 XHR 绑定。
不过要熟练使用 Ajax 进行开发,必须对 Javascript 、 XHR 相当熟悉,这对于不少程序员来说确实是个挑战。随着 Ajax 应用和研究的深入,各式各样的开源框架、包、程序库纷纷涌现。这些程序包和框架分别从各自的角度入手,试图降低 Ajax 开发的难度,比如减少 Javascript 脚本的开发量,加快开发的速度。
就目前而言,开源 Ajax 程序包和框架主要从以下几个方面对 Ajax 进行封装和扩展:
-
ü 远程调用工具包
远程调用工具包是 Ajax 框架最底层的工具包,其通常使用自己的 API 封装 XHR 对象,使得调用 XHR 更加简单直观。在 XHR 之前,我们通常使用内嵌的 IFRAME 来实现无刷新页面发送 http 请求的效果。因此,这些远程调用包必须支持那些不支持 XHR 的浏览器,以提高兼容性。
类似的工具包比如 Dojo 、 Prototype 等,他们都是纯 Javascript 的,适用于任何的服务端程序语言。 DWR 、 Ajax.NET 、 JSON-RP 等则是基于代理的 Ajax 框架,其允许 Javascript 直接与后台服务器端的类实现映射,是客户端的 Javascript 可以通过他们直接访问服务器对象。
-
ü UI 工具包和组件
基于底层的远程调用工具包,可以很容易的实现一些胖客户端所需要的常用功能、组件和效果,比如目录树、标签面板以及菜单。这些都是可复用的。典型的比如 Script.aculo.us ,其在 Prototype 的基础上,创建了大量不同类型的 UI 效果,比如 Opacity (不透明性)、 Movement (移动)、 Highlight (高显)等等。
-
ü Web 应用程序工具包
这些工具包的目的在于模仿操作系统典型的 UI 效果,以创造与桌面应用程序更加接近的用户体验。典型的比如 SmartClient ,其所提供的组件能模拟 Windows 或者 Mac OSX 的 UI 效果。
-
ü 基于自定义标签的工具包
这些工具包利用 JSP 支持自定义标签的特点,通过自定义标签封装 Ajax 应用,使传统的 HTML 控件在必要的时候具备 Ajax 功能,比如以异步方式处理表单的提交和响应,打开超链接内容的时候不重新加载页面等等。通过这些工具包,我们能够将复杂的功能封装在 HTML 风格的特定标签中,使用 Java 代码自动处理格式化、访问外部资源等 HTML 和 Javascript 所无法处理的任务。典型的比如 Ajax Tags 。
-
ü Ajax 化的 Web 框架
当前主流的 Web 框架都意识到 Ajax 的价值所在,纷纷提供对 Ajax 的支持。逐渐的,我们也可以根据自己所使用的 Web 框架,从开源项目中寻找符合要求的 Web 框架扩展。比如, Struts Ajax Tags 就提供对 Struts 标签的扩展。
-
ü 代码动态生成
在 Ruby on Rails 社区, Ruby helper 功能能够自动生成基于 Prototype 的 Javascript 代码;而 Webwork2 ,则利用 Dojo 工具包,在 Java 平台上实现同样的功能。
-
ü 基于组件
在 .NET 领域,已经有 Ajax.NET 、 Atlas 等可复用的 Ajax 组件,其允许使用拖拉的方式在 IDE 的设计视图中快速创建包含 Ajax 功能的组件;同样的, Java 领域的 JSF 、 Tapestry 等框架也提供了类似的组件。这些组件提供了快速开发 Ajax 应用的另一捷径。
在接下来的内容中,我们选取当前比较流行的三个开源程序包和框架,来体验一下 Ajax 的魅力。
(
作者介绍:柯自聪,软件工程师,专注于 Web 应用程序开发,关注 OA 、门户、电子政务、电子商务领域,著有《 Ajax 开发精要 -- 概念、案例与框架》一书以及《 Ajax 开发简略》、《 Liferay Portal 二次开发指南》等开源文档。)
二 Prototype
2.1 什么是Prototype
Prototype 是由 Sam Stephenson 开发的一个 Javascript 类库,也是其他框架的鼻祖。其对现有的部分 Javascript 对象比如 Object 、 Function 、 Dom 、 String 等进行扩展,并且对 Ajax 应用进行封装,借此提供了兼容标准的更加易于使用的类库,极大的方便开发人员快速创建具备高度交互性的 Web2.0 胖客户端应用程序。
Prototype 最初的目标是应用于 Ruby 领域的,不过由于优秀的表现和完美的封装以及服务器语言无关性,现在已经被应用到各个领域,包括 Java 、 .NET 、 PHP 等。不过在 Prototype 的源码中,还是可以找到 Ruby 的影子,比如 Ruby 样式的 Array 对象枚举。
正如之前提到的, Prototype 是一个底层的远程调用包,虽然其仅仅是一个千余行的 Javascript 文件,但是它为其他框架提供了最底层的 Javascript 扩展和 Ajax 封装。其他 Javascript 程序库在其基础上构建了更加高级的功能和 UI 效果,比如 Script.aculo.us 。
Prototype 目前的最新版本是 1.4 , 1.5 也已经提供了 pre 版本,其官方网站提供了最新版本的下载,包括 zip 包、 js 文件和 Subvision 源码。不过和其他版本一样, Prototype 官方网站并未提供完整的参考文档,开发者只能通过阅读源码掌握其功能。可喜的是,网上已经流传着不少关于 Prototype 源码解读和使用的文档,这在一定程度上弥补了 Prototype 官方文档不足的遗憾。
2.2 软件组织架构以及应用
Prototype 主要包括三个内容:一是提供了一些全局性的函数,替代原先烦琐重复的代码;二是对现有 Javascript 、 DOM 对象的扩展,提供访问公共函数的捷径;三是对 Ajax 应用的封装,使得开发 Ajax 应用更加容易和快速。
全局性的函数,比较有代表性的 $ 系列函数和 Try.these() 函数。
$() 函数是用于替代在 DOM 中频繁使用的 document.getElementById() 方法的,其返回参数传入的 id 所指向的元素。不过,其允许传入多个 id 作为参数,然后返回一个其 id 指向的元素的 Array 对象。
$F() 函数则用于返回任何表单输入控件的值,比如文本框、文本区域、下拉列表,其也是以元素的 id 或者元素本身作为参数。不过,必须注意的是, id 所指向的元素必须支持 value 属性,比如文本框。如果 id 指向一个按钮,那自然就得不到所要的 value 值。
$A() 函数能够将其接受到的任何可枚举列表转化成为一个 Array 对象,比如将 string 字符串转化成 Array 数组。 $H() 函数则将传入的对象转换成一个可枚举的和联合数组类似的 Hash 对象。 $R() 函数是 new ObjectRange(lowBound, upperBound, excludeBounds) 的缩写和替代。
Try.thiese() 方法以一系列的函数作为参数,按照顺序一个一个的执行,返回第一个成功执行的函数的返回值。这使得想调用不同的方法直到其中一个成功执行的需求变得容易和直观。否则我们就得变通的用 if else 去判断了。典型的比如在保证浏览器兼容的情况下实例化 XHR 对象。
Prototype 对 Javascript 的 Object 、 Number 、 Function 、 String 、 Array 、 Event 等对象进行了扩展,创建了一些新的对象和类,并在此基础上提供了很多有用的公共函数,比如 each() 、 any() 、 collect() 等。
Prototype 另外一个值得称道的是对 Ajax 的封装和简化,这也是 Prototype 吸引我们的另外一个重要之处。 Prototype 的 Ajax 功能主要由 Ajax.Request 和 Ajax.Updater 两个类完成。
在没有使用 Prototype 之前,我们需要创建 XHR 对象实例并且异步的跟踪其进程,在回调函数中使用 DOM 解析其返回的响应数据并且处理后更新页面。而 Ajax.Request 类提供了完成这一系列步骤的捷径。我们只需要将目标 URL 、 URL 参数、 http 请求方法类型、回调函数名称等一股脑的传递给 Ajax.Request 类即可。
Ajax.Request 类是针对需要解析服务器返回的响应数据的情况。而如果服务器返回的信息已经是 HTML 格式,只需要填充到某个 HTML 控件中,则可以使用 Ajax.Updater 类。其调用 innerHTML 直接将 HTML 代码填充到指定的 HTML 控件内部。
难得可贵的是,以往我们需要判断 XHR 的 readyState 和 status 值来获取 http 请求的状态并且作出相应的处理,以便应付请求失败的情况;而 AjaxRequest 和 Ajax.Updater 类提供了 onComplete 来替代这些烦琐的判断,其只需要简单的在请求的选项参数中的名为 onXXXX 属性 / 方法中提供自定义的方法对象即可。
接下来,我们使用 Prototype1.4 ,列举一二,体验一下 Prototype 的主要功能及其所带来的便捷。
2.3 循序渐近
从 Prototype 官方网站
http://prototype.conio.net 下载最新的开发包 prototype-1.4.0.js ,放到应用程序目录中,通过 <script> 代码引入 Prototype 程序库:
<script language="javascript" type="text/javascript" src="prototype-1.4.0.js"></script>
2.3.1 $ 系列函数体验
在 Prototype 出现之前,我们使用这种方式定位页面上的某个 HTML 元素及其值:
var myElement = document.getElementById(“your element’s id”);
var myValue = document.getElementById(“your element’s id”).value;
现在,可以分别使用 $() 函数和 $F() 函数来代替,例程 1 展示 $() 和 $F() 函数的用法:
var myElement = $(“your element’s id”);
var myValue = $F(“your element’s id”);
例程 1
: $()
和 $F()
函数的用法
<p>Username:<input type="text" name="txtUsername" value="Jimmy"></p>
<p>
<input type="button" name="$Test" value=" $ " onClick="window.alert($('txtUsername'))">
<input type="button" name="$FTest" value=" $F " onClick="window.alert($F('txtUsername'))">
</p>
$A() 函数则将其接收到的可枚举的任何参数转化成为一个 Array 对象。结合 Prototype 对 Array 的扩展, $A() 能够提供更加强大的功能。例程 2 使用 $A() 函数获取页面中的全部 input 类型的控件,并使用扩展后的 each() 函数遍历全部的控件。
例程 2
: $A()
函数的用法
<script language="javascript" type="text/javascript">
/*$A 函数体验 */
function do$ATest() {
var nodeList = document.getElementsByTagName("input");
var nodeArray = $A(nodeList);
var message = " 全部 input 控件: /r/n";
nodeArray.each(
function(node) {
message += node.type + "|" + node.name + "|" + node.value + "/r/n";
}
);
window.alert(message);
}
</script>
<input type="button" name="$ATest" value=" $A " onClick="do$ATest()">
2.3.2 Try.these() 函数的妙用
我们知道, XHR 是 Ajax 的核心之一。但是各个浏览器对 XHR 的实现不同, IE 浏览器的各个版本对 XHR 的支持也有所差异。为了保证 Ajax 的浏览器兼容性,在实例化 XHR 对象的时候,通常要使用 try/catch 对兼容性进行判断。比如例程 3 所示。
例程 3
:使用 try/catch
块实例化 XHR
var xhr = null;
if(window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
if(xhr.overrideMimeType) xhr.overrideMimeType(“text/xml”);
}
else if(window.ActiveXObject) {
try {
xhr = new ActiveXObject(“Msxml2.XMLHTTP”);
}catch(e) {
try {
xhr = new ActiveXObject(“Microsoft.XMLHTTP”);
}catch(e){}
}
}
而现在,使用 Try.these() 函数,这些烦琐的过程变得异常简单。
例程 4
:使用 Try.these()
函数实例化 XHR
function doInitialXHR() {
return Try.these(
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')},
function() {return new XMLHttpRequest()}
) || false;
}
2.3.3 对集合类的遍历
之前提过, Prototype 最初的应用领域是 Ruby 。 Ruby 为遍历集合中的元素提供了一系列快捷的方法,使得执行维护、查找、收集、删除其中的元素更加快速。 Prototype 新建了一个 Enumerable 对象,为 Javascript 提供类似 Ruby 的功能。
在 Ruby 、 .NET 语言中,都支持使用 each 关键词对集合中的元素进行遍历。在 Enumberable 对象中,还有很多方法比如 all() 、 any() 、 collect() 等都是基于 each() 方法实现的。所以, each() 方法是操作集合元素的基础。
each() 方法使用 iterator 依次获取集合中的每个元素,并将其作为匿名函数的参数;也可以在该匿名函数中加上可选参数 index ,获取当前元素的索引值。其实在例程 2 中,我们已经使用了 each() 方法。
例程 5 使用 each() 方法,对一个保存货物价格的数组进行遍历,显示价格及其索引值。
例程 5
:使用 each()
方法遍历集合
function doEachTest() {
var prices = [100.2, 445, 552.3, 56.0];
prices.each(
function(price, index) {
window.alert("Value:" + price + "| Index:" + index);
}
);
}
2.3.4 P rototype 的Ajax体验
Prototype 将 Ajax 应用封装为 Ajax.Request 和 Ajax.Update 类。使用这两个类,可以应付大部分的 Ajax 应用,而且不需要烦琐的实例化 XHR 、监控请求状态的过程。
假设我们将书籍的信息保存在一个 XML 文档中,如例程 6 所示。
例程 6
:保存书籍信息的 XML
文档
<?xml version="1.0" encoding="gb2312"?>
<books>
<book>
<title>Ajax bible</title>
<pages>500</pages>
</book>
<book>
<title>Build with Ant</title>
<pages>632</pages>
</book>
<book>
<title>Google</title>
<pages>934</pages>
</book>
</books>
现在,我们使用 Ajax.Request 类去获取这个 XML 文档的内容,并将其显示出来。例程 7 展示了这一过程。
例程 7
:使用 Ajax.Request
获取 XML
文档内容
<script language=”javascript” type=”text/javascript”>
/*Ajax.Request 类体验 */
function doAjaxRequest() {
var url = "books.xml";
var myAjax = new Ajax.Request(
url,
{
method:"get",
onComplete:showResponse
}
);
}
/*Ajax.Request 类回调函数 */
function showResponse(request) {
window.alert(request.responseText);
}
</script>
<input type="button" name="ajaxRequest" value="ajaxRequest" onClick="doAjaxRequest()">
图 1 展示了使用 Ajax.Request 后所获取的 books.xml 文档内容。
图 1 使用 Ajax.Request 后所获取的 books.xml 文档内容
例程 7 中, onComplete 指定的 showResponse 函数其实是 Ajax 的回调函数,通常在这个回调函数中完成对响应数据的解析和显示。而如果服务器端返回的是已经格式化后的 HTML 代码(这点在 Ruby 中很流行),则可以使用 Ajax.Updater 。例程 8 使用 Ajax.Updater 将服务器的响应数据填充到指定的 div 中。图 2 展示了使用 Ajax.Updater 的执行效果。
例程 8
:使用 Ajax.Updater
获取服务器的响应数据
<script language=”javascript” type=”text/javascript”>
/*Ajax.Update 类体验 */
function doAjaxUpdate() {
var url = "response.jsp";
var pars = "field=all&show=true";
var myAjax = new Ajax.Updater(
"divContent",
url,
{
method:"get",
parameters:pars
}
);
</script>
<input type="button" name="ajaxUpdate" value="ajaxUpdate" onClick="doAjaxUpdate()">
<p><div id="divContent"></div></p>
图 2 使用 Ajax.Updater 的执行效果
例程 9 是例程 8 所请求的 JSP 文件。其简单的打印出加粗后的“ Ajax.Update ”字样。
例程 9
:
<%@ page contentType="text/html; charset=gb2312"
language="java" import="java.sql.*" errorPage="" %>
<%="<strong>Ajax.Update</strong>"%>
三 Dojo
3.1 什么是Dojo
Dojo 是一个底层的 Javascript 工具包,其设计的目的是要使 Web 应用程序中增加动态特性更加容易和快捷。其提供了很多漂亮的窗口部件和 UI 效果,比如控件的浅入浅出、移动、拖拽、擦除、树菜单、内容面板、标签面板、面板容器、 Windows 窗口、向导窗口等等。 Dojo 所提供的强大的可靠的组件,可以使 Web 应用程序具备更加优秀的用户体验效果,响应更快,功能更多。
与 Prototype 相比, Dojo 提供了更完善丰富的文档。除了完整的 Doc 文档和 API 参考, Dojo 还提供了丰富的 Demo 示例,使得用户更真切的体会到 Dojo 的应用效果。
与 Prototype 不同的是, Dojo 本身实现了很多 UI 效果,比如浅入浅出,而这些效果在 Prototype 是由其额外的扩展实现比如 Script.aculo.us 实现的。
Dojo 的最新版本是 0.3.1 。我们可以从其官方网站下载到最新的 zip 开发包,其中包含了丰富的 demo 示例。
3.2 软件组织架构以及应用
Dojo 是由多个包组成的,其中包括:
ü dojo.io :对 Ajax 应用进行封装,提供简单的 API 实现 Ajax 功能。
ü dojo.event :提供浏览器兼容的事件体系。
ü dojo.lang :用于支持混合( mixins )及扩展对象。
ü dojo.graphics :用于实现优秀的 HTML 特效功能,比如浅入浅出、移动等等。
ü dojo.dnd :用于支持拖拽功能。
ü dojo.animation :用于支持动画效果的创建。
ü dojo.hostenv :用于提供 Javascript 扩展,比如提供 import/includes 替换原有的 src 属性。
对于 Ajax 应用来说, Dojo 将其封装为一个简单的方法, dojo.io.bind(request) 。其以一个 dojo.io.Request 对象作为输入参数,这个 dojo.io.Request 对象封装了所有的 Ajax 操作信息,比如目标 URL 、请求类型、 mimetype 、是否采取异步操作、回调函数等等。 dojo.io.Request 对象比较常用的属性包括:
url : Ajax 操作将提交 http 请求的目标 URL ;
mimetype : http 请求的 mime 类型,默认为 text/plain ;
method : http 请求的类型, get 或者是 post ;
formNode : http 请求中使用的表单节点;
content :将附着在 http 请求中传递给服务器的内容;
changeUrl : Ajax 操作完成后将替换当前 URL 的新 URL ;
useCache :是否使用 Dojo 提供的缓存;
load :指定 Ajax 的回调函数;
error :指定 http 请求发生错误时要调用的函数;
timeout :指定 http 请求超时时要调用的函数;
除了 Ajax 封装外, Dojo 所提供的窗口部件和 UI 效果也同样引人注目。图 3 和 4 展示了其中的两个 UI 效果。
图 3 Dojo 所实现的标签面板
图 4 Dojo 所实现的仿 Windows 窗口
3.3 循序渐近
从 Dojo 的官方网站
http://dojotoolkit.com 下载最新的 Dojo 的 zip 开发包 dojo-0.3.1-ajax.zip ,解压后的文件夹包含 demos 、 release 、 src 、 tests 子文件夹以及 dojo.js.uncompressed.js 、 dojo.js 等文件。其中, dojo.js.uncompressed.js 是未经过压缩和代码混淆的 Dojo 程序包, dojo.js 则是压缩后的 Dojo 程序包。两者在使用上没有任何差异,不过要研究源码还是参考前者为好。
将 dojo.js 拷贝的程序目录,通过 <script> 代码引入:
<script language=”javascript” type=”text/javascript” src=”dojo.js”></script>
3.3.1 Dojo 的Ajax体验
之前提到, Dojo 将 Ajax 应用封装为一个简单的方法调用 dojo.io.bind() 。这里,我们使用 dojo.io.bind() 来替换例程 7 所实现的功能:获取并显示服务器端的 xml 文档。
dojo.io.bind() 封装了 Ajax 的远程调用功能,其获取一个 hash 列表参数,随后使用这个参数初始化 XHR 对象,然后在其本身注册指定的回调函数。例程 10 展示了使用 dojo.io.bind() 实现例程 7 所示功能的过程。
例程 10
:使用 dojo.io.bind()
请求服务器的 XML
文档
<script type="text/javascript">
dojo.require("dojo.event.*");
dojo.require("dojo.io.*");
/* 使用回调函数显示响应数据 */
function doDojoBind() {
var url = "books.xml";
dojo.io.bind({
url:url,
load:function(type,data,evt) {showResponse(data);},
error:function(type,error) {},
mimetype:"text/plain"
});
}
/*dojo.io.bind 使用的回调函数 */
function showResponse(data) {
window.alert(data);
}
</script>
<input type="button" name="dojoIoTest" value="dojo.io.bind()" onClick="doDojoBind()">
四 DWR
4.1 什么是JSON和DWR
在 Ajax 应用中,我们经常使用 XML 文档来组织服务器的响应数据。虽然 XML 文档在范式统一和数据表示等方面有不可比拟的优势,不过将其应用于 Javascript 代码中之前,必须经过 DOM 解析。 JSON ( Javascript Object Notation )是一种用简单文本描述 Javascript 对象的开放格式标准,其使用 Javascript 数组直接量的格式来组织数据,相对 XML 文档而言更易于组织和解析,而且很容易将 JSON 对象直接转化成为 Javascript 对象。比如例程 6 所示 XML 数据可以很直观的用 JSON 格式表示,如例程 11 所示。这样我们就可以很直观的用 doc.books[0].title 来获取第一本书的标题。
例程 11
:用 JSON
替代 XML
文档表示数据
var doc =
{"books":
[
{
"title":"Ajax bible",
"pages":500
},
{
"title":"Build with Ant",
"pages":632
},
{
"title":"Google",
"pages":934
}
]}
如果要在 Ajax 应用程序中使用 JSON ,只需要以 JSON 格式组织服务器的响应数据, XHR 以文本的形式接收数据,则处理响应数据无需特定的数据解析器即可以使用 Javascript 操作数据了。
DWR ( Direct Web Remoting )是 getahead 公司开发的一个基于代理模式的 Ajax 应用框架,其允许客户端 Javascript 远程调用服务器端的 Java 类方法,执行相应的事务操作,就好像 Java 类是在本地客户端上一样。
DWR 使用自身的 API ,将远程服务器上的 Java 对象或者列表格式数据转化成为 JSON 格式的 Javascript 本地对象,以供本地客户机调用。
使用 DWR 可以有效的从应用程序代码中把 Ajax 的全部请求 - 响应循环代码消除掉,即客户端再也不需要直接处理 XHR 对象,不再需要编写对象的序列化代码或者使用第三方工具才能将对象转化成 XML ,甚至不再需要编写 Java 代码将 Ajax 请求调整成对 Java 对象的调用。
DWR 最新的版本是 1.1 ,其官方网站
http://getahead.ltd.uk/dwr/overview/dwr 提供了丰富的介绍、安装、部署文档以及足够的 demo 程序包。
4.2 软件组织架构以及应用
DWR 的结构主要包含两部分,一是运行再浏览器客户端的 Javascript ,其被用于与服务器通信;二是运行于服务器端的 Java Servlet ,其被用于处理请求并将响应结果转化为相应的格式回传给浏览器。
DWR 采取一种动态生成基于 Java 类的 Javascript 代码的新方法来实现和处理 Ajax 。这样, Web 开发人员就可以在 Javascript 中像使用本地浏览器代码一样使用 Java 代码,而实际上这些 Java 代码是运行于服务器端的并且可以自由访问 Web 服务器资源的。只是出于安全考虑, Web 开发者必须适当的配置,决定哪些 Java 类可以安全的被客户端调用。
图 5 展示了 DWR 如何利用一些类似 Javascript 的 onClick 等事件的结果来改变一个下拉框列表的内容。这个事件处理器调用一个 DWR 生成的 Javascript 函数,它和服务器端的 Java 函数是匹配的。 DWR 接着处理了 Java 和 Javascript 之间的所有远程信息,包括转换所有的参数和返回需要的值。接着 DWR 执行了相应的回调函数( populateList )。这个例子演示了如何使用 DWR 功能函数来改变网页内容。
图 5 DWR 交互过程
DWR 是作为 Web 应用程序中的 servlet 部署的。把它看作一个黑盒子,这个 servlet 有两个主要作用:首先,对于公开的每个类, DWR 动态地生成包含在 Web 页面中的 Javascript 。生成的 Javascript 包含存根函数,代表 Java 类上的对应方法并在幕后执行 XMLHttpRequest 。这些请求被发送给 DWR ,这时它的第二个作用就是把请求翻译成服务器端 Java 对象上的方法调用并把方法的返回值放在 servlet 响应中发送回客户端,编码成 Javascript 。 DWR 还提供了帮助执行常见的用户界面任务的 Javascript 工具函数。
4.3 循序渐近
有两种方式可以开始 DWR 的应用。一种是直接从其官方网站下载 DWR 的 Web 应用示范包,这是一个 war 的部署包,从中可以对 DWR 的应用效果及其部署方式有一个大概的了解。不过这种方式无法详细掌握如何将 DWR 与 Web 应用程序紧密集成。另外一种方式是根据 DWR 官方开发文档的讲解,通过一步步的部署和配置,将 DWR 集成到 Web 应用程序中。本节通过简单的示范和一个例子,讲述 DWR 的部署和集成。
DWR 采用一个 Java Servet 来处理请求并将响应结果发送给浏览器,这个 Java Servlet 需要加入到 Java Web 应用程序的部署描述文件 web.xml 。其次,其通过一个自定义的部署描述文件 dwr.xml ,控制 Java 对象与 Javascript 的转化。
第一步:安装 jar
开发包
从 DWR 官方网站
http://www.getahead.ltd.uk/dwr/ 下载 DWR 的开发包。这里采用 DWR1.0 ,其是一个简单的名为 dwr1.0.jar 开发包。将这个开发包放到
{APPLICATION_WEB_HOME}/WEB-INF/lib 目录下。如果使用 DWR1.1 ,则下载的应该是 DWR1.1 的开发包。这个开发包中包含了 DWR 运行所需的全部 Java 类及相应的 API 。 dwr1.0.jar 也可以从随书光盘 software 目录中找到。
第二步:修改 web.xml
,添加 Servlet
映射
修改
{APPLICATION_WEB_HOME}/WEB-INF 目录下的 web.xml ,将下列代码添加到 web.xml 的适当位置:
例程 12
:为 web.xml
添加 DWR
映射
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<servlet> 映射部分应该紧随 web.xml 中的其他 <servlet> 映射, <servlet-mapping> 则紧随 <servlet-mapping> 部分。
这段部署描述告诉 Web 应用程序,全部以“ /dwr/ ”起始的 URL 所指向的请求都交给 uk.ldt.getahead.dwr.DWRServlet 这个 Java Servlet 来处理。
第三步:创建 dwr.xml
在
{APPLICATION_WEB_HOME}/WEB-INF 目录下创建 dwr.xml 部署描述文件,其代码例程 13 所示:
例程 13
: DWR
部署描述文件 dwr.xml
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
"http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
<allow>
<create creator="new" javascript="JDate">
<param name="class" value="java.util.Date"/>
</create>
</allow>
</dwr>
这个 XML 文档中用到了其对应的 DTD 文档,顶部的文档声明指明当前用到的是 DWR1.0 版本。如果使用 DWR1.1 ,则应该相应的修改这个 XML 文档的文档声明。
这个部署描述文件定义什么样的 Java 类可以被 DWR 应用创建并通过 Javascript 远程调用。在上面的部署描述中,定义了可以被 DWR 创建的 Java 类 java.util.Date ,并给这个类赋于一个 Javascript 名称 JDate 。通过修改 dwr.xml ,也可以将自定义的 Java 类暴露给 Javascript 远程调用。
需要注意的是, dwr 部署描述为远程 Java 类拟定的 Javascript 名称还是有些限制的:
l 避免使用 Javascript 关键字或者保留字,因为这些用 Javascript 关键字或者保留字命名的方法会自动执行。大部分的 Javascript 关键字或者保留字也是 Java 的关键字或者保留字,比如“ try ()”不是一个合法的命名。不过还是有一部分的 Javascript 关键字或者保留字在 Java 中不被限制,比如“ delete ()”。
l 避免使用方法重载。这些重载的方法被调用的是时候有时候会引起麻烦,因为 Javascript 没有像 Java 那样的包命名机制来支持方法重载。
第四步:测试 URL
,查看部署效果
在浏览器地址栏中输入
http://localhost:8080/ajaxlab/dwr ,其页面效果应该如图 6 所示。通常,这个页面会显式在 dwr.xml 部署描述文件中定义的全部 Java 类,并且显式可以查看所有其可供远程调用的方法的列表的链接。这个页面有 DWR 动态创建。在本例中,页面上有一个 JDate 的链接,列出暴露给 DWR 的 JDate 类可供 Javascript 远程调用的方法。
图 6 DWR 部署效果
点击“ JDate ”链接,查看 Javascript 能够调用的 java.util.JDate 类的方法,其效果如图 7 所示。单击每个方法后面的“ Execute ”按钮,尝试执行这些方法。
图 7 能够被 Javascript 远程调用的方法
第五步:
使用 Javascript
远程调用 Java
类的方法
java.util.Date 类的相关方法已经暴露出来供 Javascript 远程调用。将以下的 Javascript 引用代码添加到 JSP 或者 HTML 文件中,就可以在 JSP 或者 HTML 文件中直接调用第四步配置所暴露的 java.util.Date 类的方法了。
<script language="javascript" src="/ajaxlab/dwr/interface/JDate.js"></script>
<script language="javascript" src='/ajaxlab/dwr/engine.js'></script>
<script language="javascript" src='/ajaxlab/dwr/util.js'></script>
例程 14 展示了这个过程,调用 java.util.Date 类的 toString ()方法将当前时间打印出来。这里例子包含一个普通按钮控件和两个 Javascript 函数,其中一个函数 doTest 通过 JDate.toString ()调用,取得 java.util.Date 所表示的当前时间。第二个函数 responseDate 将第一个函数所取得的当前时间以弹出窗口的形式显式出来。第二个函数的名称作为第一个函数的参数,这个类似第5章所说的回调函数。具体代码如下,其运行效果如图 8 所示:
例程 14
: sample12_1.jsp
<%@ page contentType="text/html; charset=gb2312" errorPage="" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Ch12--DWR 使用入门 </title>
<script language="javascript" src="/ajaxlab/dwr/interface/JDate.js"></script>
<script language="javascript" src='/ajaxlab/dwr/engine.js'></script>
<script language="javascript" src='/ajaxlab/dwr/util.js'></script>
<script language="javascript">
function doTest() {
JDate.toString(load);
}
function load(data) {
window.alert("Current Time : "+data);
}
</script>
</head>
<body>
<input type="button" name="count" value="cont" onClick="doTest()">
</body>
</html>
图 8 调用 JDate 的 Javascript 方法显示当前时间
五 其他开源框架
AjaxAnywhere 是 sourceforge.net 另外一个开源的 Ajax 项目,其设计的初衷是要将已有的 JSP 和 JSF 组件转换成为具备 Ajax 功能的组件,而且这一转换过程不需要复杂的 Javascript 编码。与其他解决方案项目, AjaxAnywhere 不是基于组件的,比如在 AjaxAnywhere 就找不到类似 Ajax Tags 的输入自动完成( auto-complete )组件。
使用 AjaxAnywhere 可以将页面简单的划分为多个区域( zone ),然后调用 AjaxAnywher 刷新( Refresh )那些指定的区域,而不是整个页面。 AjaxAnywhere 使用“分区刷新”的思路,其工作原理如下:
1 、使用 AjaxAnywhere 自定义标签库将一个 Web 页面划分为几个可重载的区域( reload-capable zones )。
2 、使用 AjaxAnywhere Javascript 应用编程接口( API )替代传统通信机制下表单提交方式。
3 、当请求在服务器端处理的时候,决定那些页面区域可以刷新( refresh )。这个过程可以使用基于客户端的 Javascript 或者基于服务器端的 AjaxAnywhere 应用编程接口( API )。
4 、在服务器端, AjaxAnywhere 会生成包含即将更新的 HTML 代码的 XML 文档。
5 、在客户端, AjaxAnywhere Javascript 接受这个 XML 文档,解析文档,并更新指定的页面区域。
采取这样的设计思路,可以尽可能的降低 Javascript 代码量,降低 Ajax 的开发门槛:
ü 不需要掌握和开发那么多的 Javascript 代码。
由于缺乏被广泛接受的命名习惯、格式化规则和模式,使得 Javascript 编码相对 Java/JSP 复杂许多。尤其在浏览器兼容性方面缺乏有效的调试和单元测试手段。使用 AjaxAnywhere 可以摆脱这些 Javascript 的复杂性。
ü 方便集成。
使用 AjaxAnywhere 不需要改变底层的应用程序代码。
ü 降低技术风险。
可以随时在传统的通信机制和 Ajax 之间切换,允许 Web 应用程序同时支持两种通信机制。
ü 平滑的兼容性。
再也不用在使用 Ajax 还是传统的交互方式间摇摆了,使用 Ajax AnyWhere 的 Web 应用程序可以兼容两种请求方式。
AjaxAnywhere 的客户端脚本经过了 IE 、 Mozilla Firefox 和 Opera 等浏览器的兼容性测试,能够最大程度的保证代码的浏览器兼容性。
另外,还需要注意的 AjaxAnywhere 特性是, Ajax 接收到的 Ajax 代码采用特殊的方式处理。 AjaxAnywhere 通过 eval(“”) 的方式执行这些 Javascript 代码,也可以将所定义的 Javascript 函数保存在适当的上下文( Context )中。不过,在允许 Ajax 方式重载的页面区域,不允许执行 document.write() 之类的 Javascript 语句。
允许重载的区域可能在提交请求之前就确定了,这种情况下需要重载客户端的 AjaxAnywhere.getZonesToReload() 的 Javascript 函数,不需要额外的服务器逻辑处理。
如果希望 AjaxAnywhere 重载整个文档,则重载后的 AjaxAnywhere.getZonesToReload() 函数必需返回“ document.all ”字符串。也可以在服务器端调用 AAUtils.setRefreshAll(true) 刷新整个页面。
相应的, Ajax 请求中的 response.sendRedirect() 会被转化成 Javascript 代码的 location.replace() 命令。
Ajax Tags 是一组 Jsp 标签,用来简化 Ajax 技术在 JSP 页面中的使用。其提供了一些常见功能的标签如下拉列表级联选择,用户在文本框中输入字符自动从指定的数据中匹配用户输入的字符( Google Suggestion 功能,输入建议)等。整个 Ajax Tags 构建在 JavaScript 框架之上。本节将简要介绍 Ajax Tags ,包括其提供的几个 Ajax 标签,并利用这些标签构建几个简单的应用。
Ajax Tags 是位于 sourceforge.net 上面的一个开源 Ajax 项目,其出发点在于应用标签封装的思想,实现一些常用的 Ajax 应用,提高开发效率。 Ajax Tags 提供一系列的标签,简化在 JSP 页面中使用 Ajax 的过程。 Javascript 在 Ajax 中占很大的比重,但实际上不少服务器端的开发人员掌握的 Javascript 知识并不完整(按照正常分工,页面构建应该是网页设计人员或者前端开发人员的职责)。 Ajax Tags 提供的标签,能够让开发人员将 JSP 页面与 Ajax 应用快速的集成整合。
Ajax Tags 支持如下情况下的无刷新页面更新:
ü 基于文本框的输入自动完成( autocomplete based on character input to an input field ),类似 Google Suggestion 的搜索建议功能。
ü 下拉列表级联选择( select box population based on selections made from another field ),即当选择一个下拉列表时,另一个下拉列表的列表项则根据上一个下拉列表的情况自动更新。
ü 文本高显提示( callout or balloon popups for highlighting content ),即当单击某个链接的时候,在其周围弹出文本框,显示相关提示信息。
ü 更新表单域内容,包括文本标签以及全部控件。
ü 图像关联( toggling images )。
ü 表单域状态开启和关闭( form field states on/off )。
Ajax Tags 的实现包含 Javascript 和 Java 类,其中, Java 类基于 JDK1.4 编译,需要 Servlet 容器支持;而 Javascript 文件可以在 Firefox1.1+ 和 IE5.0 以上版本环境下运行。
SWATO ( Shift Web Application To ……)是一组可复用的易集成的 Javascript/Java 库,可以快速的使 Web 应用程序的交互方式转变成 Ajax 方式。其主要特点有:
ü 通过 SWATO ,服务器端的 Java 库可以很方便的部署在支持 Servlet 2.3 的服务器上。
ü 其客户端的 Javascript 库可以运行在多种支持 XMLHttpRequest 的浏览器上。
ü 其使用 JSON 方法编组服务器上的 POJO 数据,很容易在任何 Javascript 环境中操作远程 POJO 对象,不管是使用硬编码还是与其他 Javascript 库集成。
ü 其提供简单的接口,使客户端的 Javascript 可以直接操作服务器上暴露给客户端的 POJO 对象。
ü 其使用多个可复用的控件( Javascript 模版、 Javascript 日志等),使开发 Web 应用程序更加容易。
其提供简单灵活的在 web.xml 中配置使用 Servlet 和 Filter (过滤器)的方式,集成 Spring 。
与 DWR 类似, SWATO 提供了通过客户端 Javascript 直接调用远程服务器端 Java 对象和 EJB 接口的实现。不同的是, SWATO 使用 JSON-RPC-Java 机制来实现客户端 Javascript 远程调用服务器端 Java 和 EJB 接口。
在 SWATO 中,客户端接受的数据可以是 JSON(SWATO 的后台是直接把 Java 对象映射成 JSON) ,也可以是 XML( 你可以从远端 URL 中获取 XML, 它在 SWATO 客户端引擎中被转化为 JSON) 。所以对于开发人员来说,它在客户端所要关心的只是 JavaScript 对象。 ( 结构与 Java 对象或 XML 结构对应 ) 。然后利用 SWATO 提供的一些视图组件 (AutoSuggest, Select, Form) 进行渲染。对于自定义性比较强的视图控件,你可以在前端使用 Template 引擎 (来自 TrimPath, 类似于 Velocity 的简单语法),甚至可以把它封装成你自己的组件,只要在其中实现 gotResult 与 gotError 函数,便可以很轻松的与 SWATO 远程调用功能集成。
六 推荐书目(按出版顺序排列)
6.1 Ajax 基础教程
国内出版的第一本 Ajax 书籍,重点介绍 Ajax 及相关的工具和技术,主要内容包括 XHR 对象及其属性和方法、发送请求和处理响应、构建完备的 Ajax 开发工具、使用 JsUnit 测试 JavaScript 、分析 JavaScript 调试工具和技术,以及 Ajax 开发模式和框架等,适合作为 Ajax 的入门书籍阅读。
6.2 Ajax 实战
Amazon.com 畅销书,目前 Ajax 领域最全面深入的著作。其中不仅有对于基础知识的介绍,还有对于 Ajax 开发中重大的体系架构问题的深入探讨,总结了大量 Ajax 开发中的设计模式,并讨论了框架、安全性与性能等等。书中提供了几个典型的例子,兼顾各种开发平台,这些例子的代码稍作修改就可以直接应用于项目开发之中。
6.3 Ajax 修炼之道Pragmatic Ajax-Web2.0入门
原书作者是 Ajaxian.com 的创始人, JavaOne 、 TheServerSide 等诸多高级别会议的演讲者。本书作为 Pragmatic 系列之一,从实践出发,通过实例展示 Ajax 的诸多特性,手把手教你实现 Google Map 的绚丽效果。不仅教会你 Ajax 的技术细节,同时还带你了解各种功能强大的主流 Ajax 工具包( Dojo 、 Prototype 、 Script.aculo.us 、 DWR 、 Backbase 、 SmartClient 、 Ajax.NET 、 SAJAX 、 JSON-RPC ),掌握 Ajax 实时查看源代码的方法以及进行代码调试的相应方法,学习 Ajax 的开发模式和框架。本书后半部分共用了五个章节,从服务器端编程的角度,详细讲述了 Ajax 同 PHP 、 Ruby On Rails 、 Java 和 .NET 等语言的融合,最后还介绍了与 Ajax 相类似的其他 RIA 技术以及 Ajax 的精彩未来( E4X, Canvas, SVG, 的相关应用)。本书秉承了《程序员修炼之道》的特点—— “从小工到专家”,各个层次的 web 开发人员都能从本书中获益匪浅。
6.4 Ajax 开发精要-概念、案例与框架
本书从概念、案例与框架三个角度来详细阐述 Ajax 开发技术,先介绍 Ajax 的由来、优势及其在 Web 开发中的地位,接着详细介绍 Ajax 的各项组成技术、封装、开源和商业框架等各种相关知识,并提供丰富实用的开发案例和综合案例,引导读者一步步地了解并掌握利用 Ajax 进行 Web 应用程序开发的方方面面。
6.5 Ajax 高级程序设计
本书是关于 Ajax 技术、模式和使用场景的开发人员级教程,不仅介绍了 Ajax 的基本知识,还讨论了 Ajax 模式和框架,同时针对 XML( 包括 XPath 和 XSLT) 、 RSS / Atom 、 Web 服务、 JSON 和 Web 用户界面组件等主题,阐述了如何将 Ajax 与这些技术有效地结合在一起,并给出了一个使用 Ajax 开发的 Web Mail 系统完整实例。书中经典的实例、完整的源代码,都将给读者带来“实战”的指导。
七 参考资源网站
点评: Jesse James Garrett 在这篇文章中第一次将这种融合多种技术的综合技术简称为 Ajax 。在这篇文章中, Jesse James Garrett 分析了现有 Web 应用程序的缺陷,定义了 Ajax 各个组成技术的职责,并且说明 Ajax 为何如此不同, Ajax 都应用在什么地方。最后, Jesse James Garrett 给出一份 FAQ ,解答人们对 Ajax 的各种问题。
7.2 Dojo 官方网站
7.3 Prototype 官方网站
7.4 DWR 官方网站
7.5 Script.aculo.us 官方网站
7.6 JSON-RPC 官方网站
7.7 Java 开源Ajax开发组件类别列表
点评: open-open.com 一个专门收集 Java 领域开源项目的网站。其将各个开源项目分门别类,提供项目介绍和导航。该页面介绍了当前大部分的 Ajax 开源项目,从中可以一窥 Ajax 开源的大致现状。
7.8 Ajax 中国
点评:《 Ajax 实战》一书的译者,国内最早关注 Ajax 应用的群体之一。站点上包含了大量介绍 Ajax 技术以及 Ajax 应用心得和经验的文章,值得 Ajax 开发人员参考和借鉴。
点评:
AjaxPatterns.org
是一个收集
Ajax
设计模式的站点,其已经发展成为
Ajax
技术的
WIKI
,内容涉及
Ajax
设计模式、
Ajax
书籍介绍、页面架构、框架、工具等等,是跟踪
Ajax
技术、提高
Ajax
开发设计水平的理想场所。