- 浏览: 1084985 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (695)
- 心情日记 (14)
- AS开发工具 (12)
- 文章转载 (99)
- AIR (5)
- 问题总结 (46)
- SWF格式 (7)
- 测试总结 (10)
- 外文资料 (9)
- 算法技术 (33)
- AS3常用开源库 (43)
- 源码范例 (102)
- FLEX (72)
- FLASH 优化 (33)
- 游戏开发 (49)
- 开发技术 (11)
- 工作应用 (34)
- AS3收集 (140)
- WebBase (0)
- 开发构想 (4)
- 设计模式 (2)
- 框架和框架范例 (19)
- RED5 (3)
- java开发 (3)
- JAVA (1)
- FLASH-3D (23)
- 3D (6)
- 书籍 (10)
- 业界信息资料 (3)
- C# (1)
- JavaScript (12)
- HTML5 (6)
- Flixel (1)
- D5Power RPG网页游戏引擎 (0)
- ColorMatrixFilter - 获得相应颜色的色调 函数 (0)
- Starling (0)
最新评论
-
老顽童203:
字体
水果忍者鼠标跟随特效制作[转载] -
hairball00:
[转] 放出超多的Flash组件源代码 -
he74552775:
flash AS3 RegExp简单功能用法(转) -
hanshuai1232000:
第四点,有利也有弊,等你做了大型的aprg,你就知道了
[转]位图数据内存优化 -
yangfantao:
太感谢
[转] 放出超多的Flash组件源代码
http://www.ibm.com/developerworks/cn/java/j-ajax1/
Ajax(即异步 JavaScript 和 XML)是一种 Web 应用程序开发的手段,它采用客户端脚本与 Web 服务器交换数据。所以,不必采用会中断交互的完整页面刷新,就可以动态地更新 Web 页面。使用 Ajax,可以创建更加丰富、更加动态的 Web 应用程序用户界面,其即时性与可用性甚至能够接近本机桌面应用程序。
Ajax 不是一项技术,而更像是一个 模式 —— 一种识别和描述有用的设计技术的方式。Ajax 是新颖的,因为许多开发人员才刚刚开始知道它,但是所有实现 Ajax 应用程序的组件都已经存在若干年了。它目前受到重视是因为在 2004 和 2005 年出现了一些基于 Ajax 技术的非常棒的动态 Web UI,最著名的就是 Google 的 GMail 和 Maps 应用程序,以及照片共享站点 Flickr。这些用户界面具有足够的开创性,有些开发人员称之为“Web 2.0”,因此对 Ajax 应用程序的兴趣飞速上升。
在这个系列中,我将提供使用 Ajax 开发应用程序需要的全部工具 。在第一篇文章中,我将解释 Ajax 背后的概念,演示为基于 Java 的 Web 应用程序创建 Ajax 界面的基本步骤。我将使用代码示例演示让 Ajax 应用程序如此动态的服务器端 Java 代码和客户端 JavaScript。最后,我将指出 Ajax 方式的一些不足,以及在创建 Ajax 应用程序时应当考虑的一些更广的可用性和访问性问题。
更好的购物车
可以用 Ajax 增强传统的 Web 应用程序,通过消除页面装入从而简化交互。为了演示这一点,我采用一个简单的购物车示例,在向里面添加项目时,它会动态更新。这项技术如果整合到在线商店,那么用户可以持续地浏览和向购物车中添加项目,而不必在每次点击之后都等候完整的页面更新。虽然这篇文章中的有些代码特定于购物车示例,但是演示的技术可以应用于任何 Ajax 应用程序。清单 1 显示了购物车示例使用的有关 HTML 代码,整篇文章中都会使用这个 HTML。
清单1. 购物车示例的有关片断
<!-- Table of products from store's catalog, one row per item -->
<th>Name</th> <th>Description</th> <th>Price</th> <th></th>
...
<tr>
<!-- Item details -->
<td>Hat</td> <td>Stylish bowler hat</td> <td>$19.99</td>
<td>
<!-- Click button to add item to cart via Ajax request -->
<button onclick="addToCart('hat001')">Add to Cart</button>
</td>
</tr>
...
<!-- Representation of shopping cart, updated asynchronously -->
<ul id="cart-contents">
<!-- List-items will be added here for each item in the cart -->
</ul>
<!-- Total cost of items in cart displayed inside span element -->
Total cost: <span id="total">$0.00</span>
回页首
Ajax 往返过程
Ajax 交互开始于叫作 XMLHttpRequest 的 JavaScript 对象。顾名思义,它允许客户端脚本执行 HTTP 请求,并解析 XML 服务器响应。Ajax 往返过程的第一步是创建 XMLHttpRequest 的实例。在 XMLHttpRequest 对象上设置请求使用的 HTTP 方法(GET 或 POST)以及目标 URL。
现在,您还记得 Ajax 的第一个 a 是代表 异步(asynchronous) 吗?在发送 HTTP 请求时,不想让浏览器挂着等候服务器响应。相反,您想让浏览器继续对用户与页面的交互进行响应,并在服务器响应到达时再进行处理。为了实现这个要求,可以在 XMLHttpRequest 上注册一个回调函数,然后异步地分派 XMLHttpRequest。然后控制就会返回浏览器,当服务器响应到达时,会调用回调函数。
在 Java Web 服务器上,请求同其他 HttpServletRequest 一样到达。在解析了请求参数之后,servlet 调用必要的应用程序逻辑,把响应序列化成 XML,并把 XML 写入 HttpServletResponse。
回到客户端时,现在调用注册在 XMLHttpRequest 上的回调函数,处理服务器返回的 XML 文档。最后,根据服务器返回的数据,用 JavaScript 操纵页面的 HTML DOM,把用户界面更新。图 1 是 Ajax 往返过程的顺序图。
图 1. Ajax 往返过程
现在您对 Ajax 往返过程有了一个高层面的认识。下面我将放大其中的每一步骤,进行更详细的观察。如果过程中迷了路,请回头看图 1 —— 由于 Ajax 方式的异步性质,所以顺序并非十分简单。
回页首
分派 XMLHttpRequest
我将从 Ajax 序列的起点开始:创建和分派来自浏览器的 XMLHttpRequest。不幸的是,不同的浏览器创建 XMLHttpRequest 的方法各不相同。清单 2 的 JavaScript 函数消除了这些依赖于浏览器的技巧,它可以检测当前浏览器要使用的正确方式,并返回一个可以使用的 XMLHttpRequest。最好是把它当作辅助代码:只要把它拷贝到 JavaScript 库,并在需要 XMLHttpRequest 的时候使用它就可以了。
清单 2. 创建跨浏览器的 XMLHttpRequest
/*
* 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;
}
稍后我将讨论处理那些不支持 XMLHttpRequest 的浏览器的技术。目前,示例假设清单 2 的 newXMLHttpRequest 函数总能返回 XMLHttpRequest 实例。
返回示例的购物车场景,我想要当用户在目录项目上点击 Add to Cart 时启动 Ajax 交互。名为 addToCart() 的 onclick 处理函数负责通过 Ajax 调用来更新购物车的状态(请参阅 清单 1)。正如清单 3 所示,addToCart() 需要做的第一件事是通过调用清单 2 的 newXMLHttpRequest() 函数得到 XMLHttpRequest 对象。接下来,它注册一个回调函数,用来接收服务器响应(我稍后再详细解释这一步;请参阅 清单 6)。
因为请求会修改服务器上的状态,所以我将用 HTTP POST 做这个工作。通过 POST 发送数据要求三个步骤。第一,需要打开与要通信的服务器资源的 POST 连接 —— 在这个示例中,服务器资源是一个映射到 URL cart.do 的 servlet。然后,我在 XMLHttpRequest 上设置一个头,指明请求的内容是表单 编码的数据。最后,我用表单编码的数据作为请求体发送请求。
清单 3 把这些步骤放在了一起。
清单 3. 分派 Add to Cart XMLHttpRequest
/*
* 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&item="+itemCode);
}
这就是建立 Ajax 往返过程的第一部分,即创建和分派来自客户机的 HTTP 请求。接下来是用来处理请求的 Java servlet 代码。
回页首
servlet 请求处理
用 servlet 处理 XMLHttpRequest,与处理普通的浏览器 HTTP 请求一样。可以用 HttpServletRequest.getParameter() 得到在 POST 请求体中发送的表单编码数据。Ajax 请求被放进与来自应用程序的常规 Web 请求一样的 HttpSession 中。对于示例购物车场景来说,这很有用,因为这让我可以把购物车状态封装在 JavaBean 中,并在请求之间在会话中维持这个状态。
清单 4 是处理 Ajax 请求、更新购物车的简单 servlet 的一部分。Cart bean 是从用户会话中获得的,并根据请求参数更新它的状态。然后 Cart 被序列化成 XML,XML 又被写入 ServletResponse。重要的是把响应的内容类型设置为 application/xml,否则 XMLHttpRequest 不会把响应内容解析成 XML DOM。
清单 4. 处理 Ajax 请求的 servlet 代码
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)&&(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);
}
清单 5 显示了 Cart.toXml() 方法生成的示例 XML。它很简单。请注意 cart 元素的 generated 属性,它是 System.currentTimeMillis() 生成的一个时间戳。
清单 5. Cart 对象的XML 序列化示例
<?xml version="1.0"?>
<cart generated="1123969988414" total="$171.95">
<item code="hat001">
<name>Hat</name>
<quantity>2</quantity>
</item>
<item code="cha001">
<name>Chair</name>
<quantity>1</quantity>
</item>
<item code="dog001">
<name>Dog</name>
<quantity>1</quantity>
</item>
</cart>
如果查看应用程序源代码(可以从 下载 一节得到)中的 Cart.java,可以看到生成 XML 的方式只是把字符串添加在一起。虽然对这个示例来说足够了,但是对于从 Java 代码生成 XML 来说则是最差的方式。我将在这个系列的下一期中介绍一些更好的方式。
现在您已经知道了 CartServlet 响应 XMLHttpRequest 的方式。下一件事就是返回客户端,查看如何用 XML 响应更新页面状态。
回页首
用 JavaScript 进行响应处理
XMLHttpRequest 的 readyState 属性是一个数值,它指出请求生命周期的状态。它从 0(代表“未初始化”)变化到 4(代表“完成”)。每次 readyState 变化时,readystatechange 事件就触发,由 onreadystatechange 属性指定的事件处理函数就被调用。
在 清单 3 中已经看到了如何调用 getReadyStateHandler() 函数创建事件处理函数。然后把这个事件处理函数分配给 onreadystatechange 属性。getReadyStateHandler() 利用了这样一个事实:函数是 JavaScript 中的一级对象。这意味着函数可以是其他函数的参数,也可以创建和返回其他函数。getReadyStateHandler() 的工作是返回一个函数,检查 XMLHttpRequest 是否已经完成,并把 XML 响应传递给调用者指定的事件处理函数。清单 6 是 getReadyStateHandler() 的代码。
清单 6. getReadyStateHandler() 函数
/*
* 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);
}
}
}
}
HTTP 状态码
在清单 6 中,检查 XMLHttpRequest 的 status 属性以查看请求是否成功完成。status 包含服务器响应的 HTTP 状态码。在执行简单的 GET 和 POST 请求时,可以假设任何大于 200 (OK)的码都是错误。如果服务器发送重定向响应(例如 301 或 302),浏览器会透明地进行重定向并从新的位置获取资源;XMLHttpRequest 看不到重定向状态码。而且,浏览器会自动添加 Cache-Control: no-cache 头到所有 XMLHttpRequest,这样客户代码永远也不用处理 304(未经修改)服务器响应。
关于 getReadyStateHandler()
getReadyStateHandler() 是段相对复杂的代码,特别是如果您不习惯阅读 JavaScript 的话。但是通过把这个函数放在 JavaScript 库中,就可以处理 Ajax 服务器响应,而不必处理 XMLHttpRequest 的内部细节。重要的是要理解如何在自己的代码中使用 getReadyStateHandler()。
在 清单 3 中看到了 getReadyStateHandler() 像这样被调用:handlerFunction = getReadyStateHandler(req, updateCart)。在这个示例中,getReadyStateHandler() 返回的函数将检查在 req 变量中的 XMLHttpRequest 是否已经完成,然后用响应的 XML 调用名为 updateCart 的函数。
提取购物车数据
清单 7 是 updateCart() 本身的代码。函数用 DOM 调用检查购物车的 XML 文档,然后更新 Web 页面(请参阅 清单 1),反映新的购物车内容。这里的重点是用来从 XML DOM 提取数据的调用。cart 元素的 generated 属性是在 Cart 序列化为 XML 时生成的一个时间戳,检查它可以保证新的购物车数据不会被旧的数据覆盖。Ajax 请求天生是异步的,所以这个检查可以处理服务器响应未按次序到达的情况。
清单 7. 更新页面,反映购物车的 XML 文档
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 > 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 < 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");
}
到此,整个 Ajax 往返过程完成了,但是您可能想让 Web 应用程序运行一下查看实际效果(请参阅 下载 一节)。这个示例非常简单,有很多需要改进之处。例如,我包含了从购物车中清除项目的服务器端代码,但是无法从 UI 访问它。作为一个好的练习,请试着在应用程序现有的 JavaScript 代码之上构建出能够实现这个功能的代码。
回页首
使用 Ajax 的挑战
就像任何技术一样,使用 Ajax 也有许多出错的可能性。我目前在这里讨论的问题还缺乏容易的解决方案,但是会随着 Ajax 的成熟而改进。随着开发人员社区增加开发 Ajax 应用程序的经验,将会记录下最佳实践和指南。
XMLHttpRequest 的可用性
Ajax 开发人员面临的一个最大问题是:在没有 XMLHttpRequest 可用时该如何响应?虽然主要的现代浏览器都支持 XMLHttpRequest,但仍然有少数用户的浏览器不支持,或者浏览器的安全设置阻止使用 XMLHttpRequest。如果开发的 Web 应用程序要部署在企业内部网,那么可能拥有指定支持哪种浏览器的权力,从而可以认为 XMLHttpRequest 总能使用。但是,如果要部署在公共 Web 上,那么就必须当心,如果假设 XMLHttpRequest 可用,那么就可能会阻止那些使用旧的浏览器、残疾人专用浏览器和手持设备上的轻量级浏览器的用户使用您的应用程序。
所以,您应当努力让应用程序“平稳降级”,在没有 XMLHttpRequest 支持的浏览器中也能够工作。在购物车的示例中,把应用程序降级的最好方式可能是让 Add to Cart 按钮执行一个常规的表单提交,刷新页面来反映购物车更新后的状态。Ajax 的行为应当在页面装入的时候就通过 JavaScript 添加到页面,只有在 XMLHttpRequest 可用时才把 JavaScript 事件处理函数附加到每个 Add to Cart 按钮。另一种方式是在用户登录时检测 XMLHttpRequest 是否可用,然后相应地提供应用程序的 Ajax 版本或基于表单的普通版本。
可用性考虑
关于 Ajax 应用程序的某些可用性问题比较普遍。例如,让用户知道他们的输入已经注册了可能是重要的,因为沙漏光标和 spinning 浏览器的常用反馈机制“throbber”对 XMLHttpRequest 不适用。一种技术是用“Now updating...”类型的信息替换 Submit 按钮,这样用户在等候响应期间就不会反复单击按钮了。
另一个问题是,用户可能没有注意到他们正在查看的页面的某一部分已经更新了。可以使用不同的可视技术,把用户的眼球带到页面的更新区域,从而缓解这个问题。由 Ajax 更新页面造成的其他问题还包括:“破坏了”浏览器的后退按钮,地址栏中的 URL 也无法反映页面的整个状态,妨碍了设置书签。请参阅 参考资料 一节,获得专门解决 Ajax 应用程序可用性问题的文章。
服务器负载
用 Ajax 实现代替普通的基于表单的 UI,会大大提高对服务器发出的请求数量。例如,一个普通的 Google Web 搜索对服务器只有一个请求,是在用户提交搜索表单时出现的。而 Google Suggest 试图自动完成搜索术语,它要在用户输入时向服务器发送多个请求。在开发 Ajax 应用程序时,要注意将要发送给服务器的请求数量以及由此造成的服务器负荷。降低服务器负载的办法是,在客户机上对请求进行缓冲并且缓存服务器响应(如果可能的话)。还应该尝试将 Ajax Web 应用程序设计为在客户机上执行尽可能多的逻辑,而不必联络服务器。
处理异步
非常重要的是,要理解无法保证 XMLHttpRequest 会按照分派它们的顺序完成。实际上,应当假设它们不会按顺序完成,并且在设计应用程序时把这一点记在心上。在购物车的示例中,使用最后更新的时间戳来确保新的购物车数据不会被旧的数据覆盖(请参阅 清单 7)。这个非常基本的方式可以用于购物车场景,但是可能不适合其他场景。所以在设计时请考虑如何处理异步的服务器响应。
回页首
结束语
现在您对 Ajax 的基本原则应当有了很好的理解,对参与 Ajax 交互的客户端和服务器端组件也应当有了初步的知识。这些是基于 Java 的 Ajax Web 应用程序的构造块。另外,您应当理解了伴随 Ajax 方式的一些高级设计问题。创建成功的 Ajax 应用程序要求整体考虑,从 UI 设计到 JavaScript 设计,再到服务器端架构;但是您现在应当已经武装了考虑其他这些方面所需要的核心 Ajax 知识。
如果使用这里演示的技术编写大型 Ajax 应用程序的复杂性让您觉得恐慌,那么有好消息给您。由于 Struts、Spring 和 Hibernate 这类框架的发展把 Web 应用程序开发从底层 Servlet API 和 JDBC 的细节中抽象出来,所以正在出现简化 Ajax 开发的工具包。其中有些只侧重于客户端,提供了向页面添加可视效果的简便方式,或者简化了对 XMLHttpRequest 的使用。有些则走得更远,提供了从服务器端代码自动生成 Ajax 接口的方式。这些框架替您完成了繁重的任务,所以您可以采用更高级的方式进行 Ajax 开发。我在这个系列中将研究其中的一些。
Ajax 社区正在快速前进,所以会有大量有价值的信息涌现。在阅读这个系列的下一期之前,我建议您参考 参考资料 一节中列出的文章,特别是如果您是刚接触 Ajax 或客户端开发的话。您还应当花些时间研究示例源代码并考虑一些增强它的方式。
在这个系列的下一篇文章中,我将深入讨论 XMLHttpRequest API,并推荐一些从 JavaBean 方便地创建 XML 的方式。我还将介绍替代 XML 进行 Ajax 数据传递的方式,例如 JSON(JavaScript Object Notation)轻量级数据交换格式。
Ajax(即异步 JavaScript 和 XML)是一种 Web 应用程序开发的手段,它采用客户端脚本与 Web 服务器交换数据。所以,不必采用会中断交互的完整页面刷新,就可以动态地更新 Web 页面。使用 Ajax,可以创建更加丰富、更加动态的 Web 应用程序用户界面,其即时性与可用性甚至能够接近本机桌面应用程序。
Ajax 不是一项技术,而更像是一个 模式 —— 一种识别和描述有用的设计技术的方式。Ajax 是新颖的,因为许多开发人员才刚刚开始知道它,但是所有实现 Ajax 应用程序的组件都已经存在若干年了。它目前受到重视是因为在 2004 和 2005 年出现了一些基于 Ajax 技术的非常棒的动态 Web UI,最著名的就是 Google 的 GMail 和 Maps 应用程序,以及照片共享站点 Flickr。这些用户界面具有足够的开创性,有些开发人员称之为“Web 2.0”,因此对 Ajax 应用程序的兴趣飞速上升。
在这个系列中,我将提供使用 Ajax 开发应用程序需要的全部工具 。在第一篇文章中,我将解释 Ajax 背后的概念,演示为基于 Java 的 Web 应用程序创建 Ajax 界面的基本步骤。我将使用代码示例演示让 Ajax 应用程序如此动态的服务器端 Java 代码和客户端 JavaScript。最后,我将指出 Ajax 方式的一些不足,以及在创建 Ajax 应用程序时应当考虑的一些更广的可用性和访问性问题。
更好的购物车
可以用 Ajax 增强传统的 Web 应用程序,通过消除页面装入从而简化交互。为了演示这一点,我采用一个简单的购物车示例,在向里面添加项目时,它会动态更新。这项技术如果整合到在线商店,那么用户可以持续地浏览和向购物车中添加项目,而不必在每次点击之后都等候完整的页面更新。虽然这篇文章中的有些代码特定于购物车示例,但是演示的技术可以应用于任何 Ajax 应用程序。清单 1 显示了购物车示例使用的有关 HTML 代码,整篇文章中都会使用这个 HTML。
清单1. 购物车示例的有关片断
<!-- Table of products from store's catalog, one row per item -->
<th>Name</th> <th>Description</th> <th>Price</th> <th></th>
...
<tr>
<!-- Item details -->
<td>Hat</td> <td>Stylish bowler hat</td> <td>$19.99</td>
<td>
<!-- Click button to add item to cart via Ajax request -->
<button onclick="addToCart('hat001')">Add to Cart</button>
</td>
</tr>
...
<!-- Representation of shopping cart, updated asynchronously -->
<ul id="cart-contents">
<!-- List-items will be added here for each item in the cart -->
</ul>
<!-- Total cost of items in cart displayed inside span element -->
Total cost: <span id="total">$0.00</span>
回页首
Ajax 往返过程
Ajax 交互开始于叫作 XMLHttpRequest 的 JavaScript 对象。顾名思义,它允许客户端脚本执行 HTTP 请求,并解析 XML 服务器响应。Ajax 往返过程的第一步是创建 XMLHttpRequest 的实例。在 XMLHttpRequest 对象上设置请求使用的 HTTP 方法(GET 或 POST)以及目标 URL。
现在,您还记得 Ajax 的第一个 a 是代表 异步(asynchronous) 吗?在发送 HTTP 请求时,不想让浏览器挂着等候服务器响应。相反,您想让浏览器继续对用户与页面的交互进行响应,并在服务器响应到达时再进行处理。为了实现这个要求,可以在 XMLHttpRequest 上注册一个回调函数,然后异步地分派 XMLHttpRequest。然后控制就会返回浏览器,当服务器响应到达时,会调用回调函数。
在 Java Web 服务器上,请求同其他 HttpServletRequest 一样到达。在解析了请求参数之后,servlet 调用必要的应用程序逻辑,把响应序列化成 XML,并把 XML 写入 HttpServletResponse。
回到客户端时,现在调用注册在 XMLHttpRequest 上的回调函数,处理服务器返回的 XML 文档。最后,根据服务器返回的数据,用 JavaScript 操纵页面的 HTML DOM,把用户界面更新。图 1 是 Ajax 往返过程的顺序图。
图 1. Ajax 往返过程
现在您对 Ajax 往返过程有了一个高层面的认识。下面我将放大其中的每一步骤,进行更详细的观察。如果过程中迷了路,请回头看图 1 —— 由于 Ajax 方式的异步性质,所以顺序并非十分简单。
回页首
分派 XMLHttpRequest
我将从 Ajax 序列的起点开始:创建和分派来自浏览器的 XMLHttpRequest。不幸的是,不同的浏览器创建 XMLHttpRequest 的方法各不相同。清单 2 的 JavaScript 函数消除了这些依赖于浏览器的技巧,它可以检测当前浏览器要使用的正确方式,并返回一个可以使用的 XMLHttpRequest。最好是把它当作辅助代码:只要把它拷贝到 JavaScript 库,并在需要 XMLHttpRequest 的时候使用它就可以了。
清单 2. 创建跨浏览器的 XMLHttpRequest
/*
* 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;
}
稍后我将讨论处理那些不支持 XMLHttpRequest 的浏览器的技术。目前,示例假设清单 2 的 newXMLHttpRequest 函数总能返回 XMLHttpRequest 实例。
返回示例的购物车场景,我想要当用户在目录项目上点击 Add to Cart 时启动 Ajax 交互。名为 addToCart() 的 onclick 处理函数负责通过 Ajax 调用来更新购物车的状态(请参阅 清单 1)。正如清单 3 所示,addToCart() 需要做的第一件事是通过调用清单 2 的 newXMLHttpRequest() 函数得到 XMLHttpRequest 对象。接下来,它注册一个回调函数,用来接收服务器响应(我稍后再详细解释这一步;请参阅 清单 6)。
因为请求会修改服务器上的状态,所以我将用 HTTP POST 做这个工作。通过 POST 发送数据要求三个步骤。第一,需要打开与要通信的服务器资源的 POST 连接 —— 在这个示例中,服务器资源是一个映射到 URL cart.do 的 servlet。然后,我在 XMLHttpRequest 上设置一个头,指明请求的内容是表单 编码的数据。最后,我用表单编码的数据作为请求体发送请求。
清单 3 把这些步骤放在了一起。
清单 3. 分派 Add to Cart XMLHttpRequest
/*
* 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&item="+itemCode);
}
这就是建立 Ajax 往返过程的第一部分,即创建和分派来自客户机的 HTTP 请求。接下来是用来处理请求的 Java servlet 代码。
回页首
servlet 请求处理
用 servlet 处理 XMLHttpRequest,与处理普通的浏览器 HTTP 请求一样。可以用 HttpServletRequest.getParameter() 得到在 POST 请求体中发送的表单编码数据。Ajax 请求被放进与来自应用程序的常规 Web 请求一样的 HttpSession 中。对于示例购物车场景来说,这很有用,因为这让我可以把购物车状态封装在 JavaBean 中,并在请求之间在会话中维持这个状态。
清单 4 是处理 Ajax 请求、更新购物车的简单 servlet 的一部分。Cart bean 是从用户会话中获得的,并根据请求参数更新它的状态。然后 Cart 被序列化成 XML,XML 又被写入 ServletResponse。重要的是把响应的内容类型设置为 application/xml,否则 XMLHttpRequest 不会把响应内容解析成 XML DOM。
清单 4. 处理 Ajax 请求的 servlet 代码
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)&&(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);
}
清单 5 显示了 Cart.toXml() 方法生成的示例 XML。它很简单。请注意 cart 元素的 generated 属性,它是 System.currentTimeMillis() 生成的一个时间戳。
清单 5. Cart 对象的XML 序列化示例
<?xml version="1.0"?>
<cart generated="1123969988414" total="$171.95">
<item code="hat001">
<name>Hat</name>
<quantity>2</quantity>
</item>
<item code="cha001">
<name>Chair</name>
<quantity>1</quantity>
</item>
<item code="dog001">
<name>Dog</name>
<quantity>1</quantity>
</item>
</cart>
如果查看应用程序源代码(可以从 下载 一节得到)中的 Cart.java,可以看到生成 XML 的方式只是把字符串添加在一起。虽然对这个示例来说足够了,但是对于从 Java 代码生成 XML 来说则是最差的方式。我将在这个系列的下一期中介绍一些更好的方式。
现在您已经知道了 CartServlet 响应 XMLHttpRequest 的方式。下一件事就是返回客户端,查看如何用 XML 响应更新页面状态。
回页首
用 JavaScript 进行响应处理
XMLHttpRequest 的 readyState 属性是一个数值,它指出请求生命周期的状态。它从 0(代表“未初始化”)变化到 4(代表“完成”)。每次 readyState 变化时,readystatechange 事件就触发,由 onreadystatechange 属性指定的事件处理函数就被调用。
在 清单 3 中已经看到了如何调用 getReadyStateHandler() 函数创建事件处理函数。然后把这个事件处理函数分配给 onreadystatechange 属性。getReadyStateHandler() 利用了这样一个事实:函数是 JavaScript 中的一级对象。这意味着函数可以是其他函数的参数,也可以创建和返回其他函数。getReadyStateHandler() 的工作是返回一个函数,检查 XMLHttpRequest 是否已经完成,并把 XML 响应传递给调用者指定的事件处理函数。清单 6 是 getReadyStateHandler() 的代码。
清单 6. getReadyStateHandler() 函数
/*
* 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);
}
}
}
}
HTTP 状态码
在清单 6 中,检查 XMLHttpRequest 的 status 属性以查看请求是否成功完成。status 包含服务器响应的 HTTP 状态码。在执行简单的 GET 和 POST 请求时,可以假设任何大于 200 (OK)的码都是错误。如果服务器发送重定向响应(例如 301 或 302),浏览器会透明地进行重定向并从新的位置获取资源;XMLHttpRequest 看不到重定向状态码。而且,浏览器会自动添加 Cache-Control: no-cache 头到所有 XMLHttpRequest,这样客户代码永远也不用处理 304(未经修改)服务器响应。
关于 getReadyStateHandler()
getReadyStateHandler() 是段相对复杂的代码,特别是如果您不习惯阅读 JavaScript 的话。但是通过把这个函数放在 JavaScript 库中,就可以处理 Ajax 服务器响应,而不必处理 XMLHttpRequest 的内部细节。重要的是要理解如何在自己的代码中使用 getReadyStateHandler()。
在 清单 3 中看到了 getReadyStateHandler() 像这样被调用:handlerFunction = getReadyStateHandler(req, updateCart)。在这个示例中,getReadyStateHandler() 返回的函数将检查在 req 变量中的 XMLHttpRequest 是否已经完成,然后用响应的 XML 调用名为 updateCart 的函数。
提取购物车数据
清单 7 是 updateCart() 本身的代码。函数用 DOM 调用检查购物车的 XML 文档,然后更新 Web 页面(请参阅 清单 1),反映新的购物车内容。这里的重点是用来从 XML DOM 提取数据的调用。cart 元素的 generated 属性是在 Cart 序列化为 XML 时生成的一个时间戳,检查它可以保证新的购物车数据不会被旧的数据覆盖。Ajax 请求天生是异步的,所以这个检查可以处理服务器响应未按次序到达的情况。
清单 7. 更新页面,反映购物车的 XML 文档
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 > 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 < 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");
}
到此,整个 Ajax 往返过程完成了,但是您可能想让 Web 应用程序运行一下查看实际效果(请参阅 下载 一节)。这个示例非常简单,有很多需要改进之处。例如,我包含了从购物车中清除项目的服务器端代码,但是无法从 UI 访问它。作为一个好的练习,请试着在应用程序现有的 JavaScript 代码之上构建出能够实现这个功能的代码。
回页首
使用 Ajax 的挑战
就像任何技术一样,使用 Ajax 也有许多出错的可能性。我目前在这里讨论的问题还缺乏容易的解决方案,但是会随着 Ajax 的成熟而改进。随着开发人员社区增加开发 Ajax 应用程序的经验,将会记录下最佳实践和指南。
XMLHttpRequest 的可用性
Ajax 开发人员面临的一个最大问题是:在没有 XMLHttpRequest 可用时该如何响应?虽然主要的现代浏览器都支持 XMLHttpRequest,但仍然有少数用户的浏览器不支持,或者浏览器的安全设置阻止使用 XMLHttpRequest。如果开发的 Web 应用程序要部署在企业内部网,那么可能拥有指定支持哪种浏览器的权力,从而可以认为 XMLHttpRequest 总能使用。但是,如果要部署在公共 Web 上,那么就必须当心,如果假设 XMLHttpRequest 可用,那么就可能会阻止那些使用旧的浏览器、残疾人专用浏览器和手持设备上的轻量级浏览器的用户使用您的应用程序。
所以,您应当努力让应用程序“平稳降级”,在没有 XMLHttpRequest 支持的浏览器中也能够工作。在购物车的示例中,把应用程序降级的最好方式可能是让 Add to Cart 按钮执行一个常规的表单提交,刷新页面来反映购物车更新后的状态。Ajax 的行为应当在页面装入的时候就通过 JavaScript 添加到页面,只有在 XMLHttpRequest 可用时才把 JavaScript 事件处理函数附加到每个 Add to Cart 按钮。另一种方式是在用户登录时检测 XMLHttpRequest 是否可用,然后相应地提供应用程序的 Ajax 版本或基于表单的普通版本。
可用性考虑
关于 Ajax 应用程序的某些可用性问题比较普遍。例如,让用户知道他们的输入已经注册了可能是重要的,因为沙漏光标和 spinning 浏览器的常用反馈机制“throbber”对 XMLHttpRequest 不适用。一种技术是用“Now updating...”类型的信息替换 Submit 按钮,这样用户在等候响应期间就不会反复单击按钮了。
另一个问题是,用户可能没有注意到他们正在查看的页面的某一部分已经更新了。可以使用不同的可视技术,把用户的眼球带到页面的更新区域,从而缓解这个问题。由 Ajax 更新页面造成的其他问题还包括:“破坏了”浏览器的后退按钮,地址栏中的 URL 也无法反映页面的整个状态,妨碍了设置书签。请参阅 参考资料 一节,获得专门解决 Ajax 应用程序可用性问题的文章。
服务器负载
用 Ajax 实现代替普通的基于表单的 UI,会大大提高对服务器发出的请求数量。例如,一个普通的 Google Web 搜索对服务器只有一个请求,是在用户提交搜索表单时出现的。而 Google Suggest 试图自动完成搜索术语,它要在用户输入时向服务器发送多个请求。在开发 Ajax 应用程序时,要注意将要发送给服务器的请求数量以及由此造成的服务器负荷。降低服务器负载的办法是,在客户机上对请求进行缓冲并且缓存服务器响应(如果可能的话)。还应该尝试将 Ajax Web 应用程序设计为在客户机上执行尽可能多的逻辑,而不必联络服务器。
处理异步
非常重要的是,要理解无法保证 XMLHttpRequest 会按照分派它们的顺序完成。实际上,应当假设它们不会按顺序完成,并且在设计应用程序时把这一点记在心上。在购物车的示例中,使用最后更新的时间戳来确保新的购物车数据不会被旧的数据覆盖(请参阅 清单 7)。这个非常基本的方式可以用于购物车场景,但是可能不适合其他场景。所以在设计时请考虑如何处理异步的服务器响应。
回页首
结束语
现在您对 Ajax 的基本原则应当有了很好的理解,对参与 Ajax 交互的客户端和服务器端组件也应当有了初步的知识。这些是基于 Java 的 Ajax Web 应用程序的构造块。另外,您应当理解了伴随 Ajax 方式的一些高级设计问题。创建成功的 Ajax 应用程序要求整体考虑,从 UI 设计到 JavaScript 设计,再到服务器端架构;但是您现在应当已经武装了考虑其他这些方面所需要的核心 Ajax 知识。
如果使用这里演示的技术编写大型 Ajax 应用程序的复杂性让您觉得恐慌,那么有好消息给您。由于 Struts、Spring 和 Hibernate 这类框架的发展把 Web 应用程序开发从底层 Servlet API 和 JDBC 的细节中抽象出来,所以正在出现简化 Ajax 开发的工具包。其中有些只侧重于客户端,提供了向页面添加可视效果的简便方式,或者简化了对 XMLHttpRequest 的使用。有些则走得更远,提供了从服务器端代码自动生成 Ajax 接口的方式。这些框架替您完成了繁重的任务,所以您可以采用更高级的方式进行 Ajax 开发。我在这个系列中将研究其中的一些。
Ajax 社区正在快速前进,所以会有大量有价值的信息涌现。在阅读这个系列的下一期之前,我建议您参考 参考资料 一节中列出的文章,特别是如果您是刚接触 Ajax 或客户端开发的话。您还应当花些时间研究示例源代码并考虑一些增强它的方式。
在这个系列的下一篇文章中,我将深入讨论 XMLHttpRequest API,并推荐一些从 JavaBean 方便地创建 XML 的方式。我还将介绍替代 XML 进行 Ajax 数据传递的方式,例如 JSON(JavaScript Object Notation)轻量级数据交换格式。
相关推荐
面向Java开发人员的Ajax技术是构建现代Web应用程序的关键技术之一,尤其对于提升用户体验至关重要。Ajax,全称为异步JavaScript和XML,它打破了传统的Web应用程序页面重载模式,使得用户可以在不进行完整页面刷新的...
对于Java开发人员来说,理解并掌握Ajax技术是构建交互性强、用户体验良好的Web应用程序的关键。本资源面向Java开发者,旨在帮助他们利用Ajax创建动态的Java应用。 首先,Ajax的核心原理在于XMLHttpRequest对象,它...
面向Java开发人员的Ajax技术,特别是与Jetty服务器和Direct Web Remoting (DWR)框架的结合,为创建高性能、可扩展的Comet应用程序提供了强大的工具。Comet是一种Web交互模式,它允许服务器向客户端推送数据,而不...
第 12 部分:面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 第 13 部分:面向 Java 开发人员的 Ajax: Ajax 的 Java 对象序列化 第 14部分:面向 Java 开发人员的 Ajax 结合 Direct Web Remoting 使用 Ajax ...
在本文中,我们将深入探讨如何面向Java开发人员使用Ajax,特别是结合DWR框架来实现高效的数据交互。DWR允许Java对象直接在客户端JavaScript中调用,消除了传统Ajax需要通过XMLHttpRequest对象手动处理HTTP请求的繁琐...
Google Web Toolkit (GWT) 是一个强大的工具,它允许Java开发人员使用熟悉的Java语言来构建高性能、跨浏览器的Ajax应用程序。GWT通过将Java代码编译为优化过的JavaScript,解决了Web开发中的语言障碍,使得开发人员...
Java Web整合开发是构建Web应用程序的关键技术,涵盖了Java语言、Eclipse集成开发环境(IDE)、Struts 2框架以及Ajax异步JavaScript和XML技术。这个主题深入探讨了这些技术如何协同工作,创建出高效、用户友好的Web...
对于Java开发人员来说,掌握Ajax可以帮助他们构建更高效、更具交互性的Web应用程序。** Ajax的核心在于创建异步通信,它使得浏览器可以与服务器进行数据交换,而不必等待整个页面的刷新。这极大地提升了用户体验,...
### 面向Java开发人员探索Google Web Toolkit:深度解析与应用 Google Web Toolkit(GWT)是一款由Google推出的开源框架,专为Java开发者设计,旨在简化和加速Web应用程序的开发过程。它允许开发者使用Java语言编写...
Spring是一个全面的Java企业级应用框架,支持依赖注入、AOP(面向切面编程)、事务管理等。第十四章的PPT将详细介绍Spring的核心特性,以及如何在实际项目中应用。 7. **XML**: XML是一种标记语言,常用于数据...
Java Web开发是构建基于互联网应用程序的关键技术,涵盖了多种框架和工具。这个压缩包中的资源,"零基础学Java.Web开发:JSP Servlet Struts Spring Hibernate Ajax(PPT)",旨在为初学者提供一个全面的学习路径,从...
传统的Web应用开发通常将客户端和服务器端代码库分开,而GWT打破了这种界限,提供了一个Java API,开发者可以基于这个API构建基于组件的图形用户界面(GUI)。这使得使用GWT开发Web应用类似于使用Swing或SWT开发桌面...
Java、Servlet 和 Ajax 是构建动态网站的关键技术,它们共同为用户提供了交互性和实时更新的能力。本指南将深入解析这些技术,并结合实际应用,帮助您理解和掌握如何利用它们来开发高效、响应迅速的网络应用。 一、...
首先,JSP是Java的一种视图技术,它允许开发人员在HTML或XML页面中嵌入Java代码,以动态生成网页内容。Servlet则作为服务器端的Java组件,负责处理HTTP请求和生成响应,是JSP背后的执行引擎。两者结合,为Web应用...
【Java Web开发】是IT领域中的一个重要分支,它结合了Java编程语言与Web技术,用于构建动态、交互式的网络应用程序。对于初学者来说,掌握Java Web开发的基本概念和技术是迈向专业开发者的必经之路。本资源"零基础学...
【标题】"0Java.Web开发:JSP.Servlet.Struts.Spring.Hibernate.Ajax"涉及的是Java Web应用程序开发的关键技术栈,主要包括以下几个核心组件: 1. **JSP(JavaServer Pages)**:JSP是Java平台上的动态网页技术,...
Java是一种多平台、面向对象的编程语言,广泛用于构建服务器端应用程序,而Ajax(Asynchronous JavaScript and XML)则是一种用于创建动态和交互式网页的技术。让我们深入了解一下这两个技术以及它们如何协同工作。 ...
Java Web开发是一个广泛的领域,涵盖了多种技术和框架,用于构建动态、数据驱动的Web应用程序。本压缩包"Java.Web开发JSP Servlet Struts Spring Hibernate Ajax.rar"显然包含了几个关键的Java Web开发技术,包括JSP...
7. **Spring框架**:Spring是Java领域最流行的轻量级应用框架,提供依赖注入、AOP(面向切面编程)、MVC(模型-视图-控制器)等特性,简化企业级应用开发。 这些文档可能涵盖了以下内容: - Java基础:变量、数据...