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请求/响应过程的标准生命周期:
图1:jsf的生命周期
通过上图可以观察到,任何一个jsf“faces request” 请求,经过restore view、apply request values、process validations、update models、invoke application等阶段以后,产生了一个 “render response” 返回给客户端。那么,常规jsf引擎是如何实现上述过程的呢?
图2:常规jsf引擎的请求与响应过程
回顾一下常规jsf引擎针对请求与响应的过程:首先,客户端请求某个资源,产生一个faces request;服务器端接收到此请求以后,经过一系列后台处理,产生一个faces response。我们注意到:响应的content-type是text/html,而产生的内容主体是一段html文本;浏览器在接收到html文本以后,进行整个页面的渲染与刷新。
无需写ajax代码的ajax enabled应用
我用自己开发的jsf引擎,这样处理上述过程(详见参考资料www.operamasks.org ),如下图所示:
图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的请求、应答、及页面控件的更新
数据传输量明显减少
避免整个页面的刷新,更好的用户体验
系统保持敏捷、高效
换言之:任何标准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 enabled。
例如,apache myfaces的tomahawk项目提供了一个tree组件,这个组件本身并不支持ajax,每当按下一个tree结点都将重新刷新整个页面。使用
标签后,则只刷新tree部分,而不刷新页面的其他部分。当然更好的方式是,提供一个本身就支持ajax的tree组件,以减少冗余数据的传递。关于
标签的原理,有兴趣的读者可以参考operamasks jsf的源码(详见参考资料),这里就不再一一赘述了。
综上,javaee 需要ajax,但并不需要传统的ajax开发模式。通过我们提出的operamasks jsf技术,我们不再需要知道什么是ajax,而我们的应用却是自然而然的ajax enabled应用。
因此,我们认为:javaee without ajax!
(责任编辑:包春林)