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]--><!--[endif]--><!--[if !vml]--><!--[endif]-->
图
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!