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!