3 年前, “Spring 之父 ”Rod.Johnson 写了一本在 Java 界引起轰动的书:《 Expert One-on-One J2EE Development Without EJB 》。这本书阐述了 EJB 作为 J2EE 核心技术所带来的意义与价值,但作者用了更大篇幅介绍 EJB 的一些缺陷与不足,并提出了 Without EJB 的解决方案。正是由于 “J2EE Without EJB” 这个激动人心的口号及这本书奠定的基础 ,导致了 Spring Framework 这个经典轻量级框架的诞生。
2 年前, Ajax 开始进入人们的视野。时至今日, Ajax 已经成为一个红得发紫的技术。但是今天,我想说一句: JavaEE without Ajax 。
Ajax 的“原罪”
Ajax 为什么这样红?有人说,是因为起了个好听易记的名字(比如荷兰著名的 Ajax 球队,即阿贾克斯);也有人说,是因为 Google 全新的 Ajax 应用产品给人们带来的超酷体验(比如伟大的 Google Maps 、 GMail 等)。确实如此, Ajax 能够如此流行的最主要原因就是它带来了更好的用户体验,改变了人们对传统 Web 应用的不佳印象。
然而,即使 Ajax 的狂热 Fans 也不得不承认的是,从技术层面上来说, Ajax 并没有带来什么新鲜的东西。它本质上是一种新瓶装旧酒的技术,好处是通过 Java Script 与 DHTML 提供了一种异步编程模型,从而使 Web 应用给客户带来了更好的人机体验。正如我在去年 引起大家争论 的拙文《 Ajax ,只是一种过渡技术》中表述的: Ajax 解决问题的层面较低。或者说,它解决问题的方法与手段,很难形成一种可高度抽象的框架级解决方案。并且,正是因为 Ajax 基于 Java Script ,因此不可避免地带来了 Java Script 的诸多缺点,譬如:
跨浏览器是一场噩梦
对搜索引擎的支持不好
干掉了Back、History等按钮(尽管我并不认为Back、History是什么好东西)
开发与维护成本过高
要 Java, 不要 Java Script
We Love Java, Not Java Script 。套用毛泽东的惯用句式就是: “ 要 Java, 不要 Java Script” 。相信很多读者看完这个标题也许会不以为然,但这句话却代表了许多 J2EE 开发人员的心声。
众多 Java 工程师都对 Java 有一种近乎偏执的喜爱,他们热爱 Java 的简洁与优雅。但一旦让他们去进行 Java Script 的开发,却往往会不知所措:过度灵活的语法,无法通过编译器进行语法校验,缺乏良好的调试工具等等这些,都会让人们对 Java Script 畏手畏脚,更遑论 Ajax 的开发。
一句话, Java 社区需要 Ajax ,需要它 来提升基于 JavaEE 的 Web 应用的人机体验;但是,人们并不喜欢 Ajax 目前的开发模式。无疑,我们需要一种新的解决方案。
谁来拯救 JavaEE 的 Ajax ?
我给出的答案是 JSF 。目前,关于 JSF 的一种流行说法是“悲剧人生: Sun 让 JSF 光着身子降临到 Java Web 世界”。然而,我的看法却是:作为一种革命性的服务器端组件技术, JSF 犹如早晨八九点钟的太阳,前途不可限量。
让事实说话,我们先来看看 JSF 请求 / 响应过程的标准生命周期:
<!--[if !vml]-->
图 1 : JSF 的生命周期
通过上图可以观察到,任何一个 JSF“Faces Request” 请求,经过 Restore View 、 Apply Request Values 、 Process Validations 、 Update Models 、 Invoke Application 等阶段以后,产生了一个 “Render Response” 返回给客户端。那么,常规 JSF 引擎是如何实现上述过程的呢?
<!--[if !vml]--><!--[endif]-->
图 2 :常规 JSF 引擎的请求与响应过程
回顾一下常规 JSF 引擎针对请求与响应的过程:首先,客户端请求某个资源,产生一个 Faces Request ;服务器端接收到此请求以后,经过一系列后台处理,产生一个 Faces Response 。我们注意到:响应的 Content-Type 是 text/html ,而产生的内容主体是一段 HTML 文本;浏览器在接收到 HTML 文本以后,进行整个页面的渲染与刷新。
无需写 Ajax 代码的 Ajax Enabled 应用
我用自己开发的 JSF 引擎,这样处理上述过程(详见参考资料 www. OperaMasks.org ) ,如下图所示:
<!--[if !vml]--><!--[endif]-->
图 3 : OperaMasks JSF 实现的请求与响应过程
首先可以观察到, Faces Request 的发出是基于 “x-requested-by: XML Http Request” ,也就是说,这是一个 Ajax 请求,而该请求在到达服务器端以后,服务器端所产生的 Faces Response 同常规 Faces Response 相比也发生了变化: Content-Type 不再是 text/html ,变成了 text/javascript ;并且,响应的主体也不再是 html 文本,而是一堆 script 脚本。浏览器在接收到响应以后,再也不需要进行整个页面的渲染与刷新,而只仅仅需要执行这段脚本内容,将页面的控件进行更新即可。
显而易见,通过 上述 JSF 技术,我们获得了:
基于Ajax的请求、应答、及页面控件的更新
数据传输量明显减少
避免整个页面的刷新,更好的用户体验
系统保持敏捷、高效
<!--[if !supportLists]--><!--[endif]-->
换言之:任何标准 JSF 应用,只需将其在 OperaMasks JSF 引擎上运行,就可以达到这样的效果。我们并没有写任何一行 Ajax 的代码,但是,我们的应用却是自然而然的 Ajax Enabled 的应用。大道至简,大象无形。
奥妙所在: JSF 的 Render 机制
为什么可以这样?
JSF 组件只是特定状态和行为的载体,而组件以什么形式去和用户交互,是完全可定制的、独立于该特定的表现语言,可以是 HTML 、 WML 或者其他形式;具体是什么,可以通过指定 JSF 组件的 Render Kit 来实现,而每一种 Render Kit ,对应于组件作者写的同一风格和形式的一系列 Render 。
比如,如果想在网页中实现图表功能( Chart) , MSIE 有 VML , Gecko 和 Opera 有 SVG ;而在服务器端只需要简单地判断一下浏览器类型,就可以选择一个 Render Kit , 生成不同的客户端表现来完成相同功能――这是用常规 JSP 技术很难完成的任务。
通俗的说, JSF 组件可以翻译成任何你想要的形式。 So , JSF 框架比现有其它开源框架具有更强的生命力。上文所述的 OperaMasks JSF ,其容器级别 Ajax 实现,正是灵活应用 Render Kit 的具体案例。
从容器级别对 Ajax 予以支持的 JSF 引擎
我们提出的 JSF 是直接由 JSF 容器来处理 Ajax 请求的,它会根据请求类型来判断这是一个正常 HTTP 请求还是一个 Ajax 请求:如果是常规 HTTP 请求就运行 JSP 页面,生成页面文档(特定的,对于 Ajax Render kit ,要加入一些 Ajax 基础 JavaScript 代码);如果是 Ajax 请求,服务器对请求参数正常解码,并执行 JSF 中除页面输出阶段以外的所有其他阶段,生成一个 JSF 组件树。
一直到这一步为止,处理方式与对普通 HTTP 请求的处理完全一致,唯一不同的是:在随后 Render Response 阶段,容器除了调用组件作者写的 Ajax 功能 Renderer 以外,更重要的是在生成响应页面时,会过滤掉一切不会变化的静态内容――也就是说,静态内容不会生成到响应页面中去,而对每一个动态内容则会生成一个相应 JavaScript 代码(可以更进一步优化为只有变化了的动态内容才处理)。这样,传给客户的 Ajax 应答实际上是由这样一些 JavaScript 语句构成。在 Ajax 响应返回到客户端时,就可以自动由 Ajax 回调函数执行这些 JavaScript 语句,完成对页面即时的、局部的更改,而不需要刷新整个页面。依赖 JSF 组件的具体功能,甚至可以改变页面的外观。而整个 Ajax 机制由 JSF 引擎提供,对用户完全透明。
实际上,在 JSF 规范中 JSF 页面输出阶段所采用的 Render Kit 是可替换的,默认的 HTML_BASIC Render Kit 输出的是标准 HTML 语法,不包含任何 Java Script 代码。 我们提出的 JSF 引擎实现了一个 Ajax Render Kit ,可以在 HTML 文档中嵌入 Java Script 代码来实现 Ajax 特性,而替换 Render Kit 只需要修改配置文件即可。
简单地说, 这种 JSF 引擎为每个标准组件都实现了相应的 Ajax Render , 比如对 UICommand 组件,其 Ajax Render 会在 onclick 事件中加入 JavaScript 的 Ajax 提交代码,向服务器提交 Ajax 请求。通过这种方式,任何一个包含标准 JSF 组件的 Web 应用,都可以通过只更改 Render Kit 配置为 Ajax 来实现 Web 应用 Ajax 化。而对于第三方的组件,可能本身并不支持 Ajax ,但使用一个名为 <Ajax:renderGroup> 的标签,就可以立即将这个第三方组件转换成 Ajax Enabled 。
例如, Apache myfaces 的 Tomahawk 项目提供了一个 Tree 组件,这个组件本身并不支持 Ajax ,每当按下一个 Tree 结点都将重新刷新整个页面。使用 <Ajax:renderGroup> 标签后,则只刷新 Tree 部分,而不刷新页面的其他部分。当然更好的方式是,提供一个本身就支持 Ajax 的 Tree 组件,以减少冗余数据的传递。关于 <Ajax:renderGroup> 标签的原理,有兴趣的读者可以参考 OperaMasks JSF 的源码(详见参考资料),这里就不再一一赘述了。
综上, JavaEE 需要 Ajax ,但并不需要传统的 Ajax 开发模式。通过我们提出的 OperaMasks JSF 技术,我们不再需要知道什么是 Ajax ,而我们的应用却是自然而然的 Ajax Enabled 应用。
因此,我们认为: JavaEE Without Ajax!