使用标记库大量使用标记库(taglib)在 Java 的 Web 应用程序开发中是很普遍的。像许多 Java EE Web 框架一样,有些标记库现在也不支持异步通信模型,没有把通过
XMLHttpRequest 提交的数据转换成
HttpServletRequest (以及反过来)的途径。实际上,不支持异步通信的标记库,在 Ajax
XMLHttpRequest 调用期间不工作。可用的选项有:
- 放弃使用不支持异步模型的标记库:把现在由标记库生成的代码迁移到 HTML/JavaScript 代码。(如果 Web 应用程序高度依赖标记库,这种方式最终会造成视图层页面尺寸的增大。)
- 解决问题:使用已经解决了这个问题的 Ajax 框架。示例之一就是 DWR(请参阅 ExecutionContext.forwardToString())。 在这种情况下,可以继续使用以前一直使用的标记库。
- 使用支持 Ajax 的标记库:使用支持异步模型的标记库,例如 Ajax JSP 标记库(AjaxTags)(请参阅 参考资料)。
用 IDE 进行开发与调试有许多 JavaScript 调试工具可以帮助开发人员开发 JavaScript 解决方案。但是,传统的 Java 开发环境不允许检查
XMLHTTPRequest 和与 Ajax 相关的其他东西的值。
一个解决方案是利用 AJAX Toolkit Framework(ATF)(请参阅
参考资料)。ATF 是个 Eclipse 插件,带有增强的 JavaScript 编辑特性,例如编辑时语法检查,内嵌的 Mozilla Web 浏览器,内嵌的 DOM 浏览器,内嵌的 JavaScript 调试器。ATF 还包含 Personality Builder 功能,这个功能可以帮助任何 Ajax 运行时框架构建 IDE 特性,从而加入到 ATF 支持的运行时环境集中。
线程问题在典型的同步 Web 应用程序中,有些领域对按钮或链接点击要求更长一点的处理时间。没有耐心和没经验的 Web 用户通常会不止一次地点击按钮或链接,以为可以帮助加快处理速度,从而引发多重表单提交。其他时候,用户认为需要双击(就像桌面应用程序那样)。Web 应用程序中的多重表单提交在某些情况下是无害的。而在其他情况下,副作用可能造成严重的线程问题或争用情况(此时多个线程竞争执行一个代码块)。例如,在银行应用程序中多次点击转帐按钮,可能造成不希望的多次转帐。
既支持同步通信模型又支持异步通信模型的 Web 应用程序会发现,如果它的功能没有正确分析和规划,那么自己就处在了相似的困境中。支持两种通信模型的应用程序在某个页面上可能混合了服务器端调用(即,或者同步,或者异步,或者混合了同步和异步)。就像在多重点击场景中一样,异步调用可能处理得慢些。如果应用程序不做预防,用户可能会在异步线程正在处理时又调用了一个同步调用,因为页面没有刷新,所以无法阻止页面上的进一步活动。结果是两个线程并发处理。虽然不是由 Web 页面上的同一按钮或链接引发,这类情况还是会造成服务器端的线程问题(与多重点击问题类似)。
例如,以图 6 所示的银行应用程序的转账页面为例:
图 6. 转帐示例
对于这个示例,用红色表示的转账按钮引发一个 Ajax 调用。退出链接(黄色)引发同步调用。如果不耐烦或没有经验的用户点击了红色按钮之后接着又点击黄色链接(假设两个链接在代码中有共同的路径),就会发生争用情况。
一般来说,有两种方式可以避免这类情况发生。第一种方式是客户端解决方案。一旦点击了一个链接或按钮,就用 JavaScript 确保禁止后续的页面提交,直到当前线程执行完成。第二个解决方案是允许多线程提交,但是依赖于服务器端代码中的同步来避免争用情况。如果引入同步来解决这个问题,请记住 Java EE Web 组件(servlet、portlet、JSF 等)是多线程的。要当心大段代码的同步(特别是与请求/响应生命周期处理有关的代码)。在效果上,同步的误用,会把应用程序变成单线程应用程序,从而降低吞吐率。
克服性能缺陷使用 Ajax 还有可能影响基于 Java EE Web 的应用程序的性能。允许每个请求上有额外线程的可能性,可能会影响两个资源。
首先,servlet 容器的
线程池 可能受到影响。线程池指定 Web 容器中允许并发运行的线程的最大数量。每个客户机请求需要一个线程。但是,一个客户机请求不一定等于一个用户请求。浏览器可能为一个用户请求要求多个客户机请求。例如,提交表单的一个用户可能要求多个客户机请求(其中包含提交表单的值、检索 GIF 文件、检索 JavaScript 文件、检索 CSS 文件)。如果允许并发地提交同步和异步请求,就意味着每个用户请求至少要支持多出一个的线程消耗(用于 Ajax 请求)。虽然为每个用户请求多增加一个线程的可能性看起来不多,但是当应用程序处在负载之下时,影响就明显了(这时每个用户请求多出的一个额外线程乘上平均用户数量)。显示,这种情况有可能影响 servlet 容器的性能。
另一个可能受影响的资源是
数据库连接池。典型的 Java EE Web 应用程序支持一个用户请求的两类序列:
浅(shallow)请求和
深(deep)请求。浅请求是执行服务器端代码但是不访问持久性存储(例如数据库)就完成请求的 Web 页面发出的请求。深请求是执行服务器端代码并访问持久性存储才能完成请求的 Web 页面发出的请求。
在深请求序列中(假设需要数据库连接),数据库连接池的这些方面可能会由于允许多个线程而受到影响:
- 等待连接的线程的平均数量
- 以毫秒为单位的连接的平均等候时间
- 连接被使用的平均时间
所以,可能需要提高连接池的平均大小或连接数量。
对付测试Java 开发人员一直在不断地努力为 Java SE 和 Java EE 代码提供单元测试工具。由于 Ajax 的引入造成浏览器内的 JavaScript 增多,对可靠的 JavaScript 单元测试框架也提出了要求。现在可用的框架有 JsUnit、Selenium 和 HttpUnit(请参阅
参考资料)。
这些框架提供了为 JavaScript 函数开发单元测试的工具,可以操纵 Web 页面上的 DOM 元素。它们允许把单元测试组织成测试套件。Selenium 的浏览器兼容性测试特性允许在不同的浏览器和操作系统上测试 JavaScript 函数。它利用 JavaScript 和 Iframe 在浏览器中嵌入了测试自动引擎。这项技术应当可以在任何支持 JavaScript 的浏览器中工作,对于支持多个浏览器和浏览器版本的应用程序来说特别有用。Selenium 和 JsUnit 都支持
持续集成:可以把 JavaScript 单元测试和测试套件集成到自动构建过程。
结束语把 Ajax —— 就像其他技术或模式一样 —— 引入 Java EE 应用程序,也有它的优势和不足。本文提供了把 Ajax 集成到 Java EE Web 应用程序的概述。Ajax 的异步通信模型与传统 Java EE Web 应用程序内置就支持的同步模型有很大不同。为了避免盲目,请确保在采用 Ajax 之前对潜在的问题领域有全面的事前规划。
Java EE 框架对 Ajax 的支持和工具在不断改进。未来有望会有框架自带的 Ajax 支持来降低集成的复杂性。基于 JSF 的 Apache Shale 和基于 servlet 的 DWR 是两个希望您保持关注的框架。