2 年前, Ajax 開始進(jìn)入人們的視野。時(shí)至今日, Ajax 已經(jīng)成為一個(gè)紅得發(fā)紫的技術(shù)。但是今天,我想說一句: JavaEE without Ajax 。
Ajax 的“原罪”
Ajax 為什么這樣紅?有人說,是因?yàn)槠鹆藗€(gè)好聽易記的名字(比如荷蘭著名的 Ajax 球隊(duì),即阿賈克斯);也有人說,是因?yàn)?Google 全新的 Ajax 應(yīng)用產(chǎn)品給人們帶來的超酷體驗(yàn)(比如偉大的 Google Maps 、 GMail 等)。確實(shí)如此, Ajax 能夠如此流行的最主要原因就是它帶來了更好的用戶體驗(yàn)" title="用戶體驗(yàn)">用戶體驗(yàn),改變了人們對(duì)傳統(tǒng) Web 應(yīng)用的不佳印象。
???? 然而,即使 Ajax 的狂熱 Fans 也不得不承認(rèn)的是,從技術(shù)層面上來說, Ajax 并沒有帶來什么新鮮的東西。它本質(zhì)上是一種新瓶裝舊酒的技術(shù),好處是通過 Java Script 與 DHTML 提供了一種異步編程模型,從而使 Web 應(yīng)用給客戶帶來了更好的人機(jī)體驗(yàn)。正如我在去年引起大家爭論的拙文《 Ajax ,只是一種過渡技術(shù)》中表述的: Ajax 解決問題的層面較低。或者說,它解決問題的方法與手段,很難形成一種可高度抽象的框架級(jí)解決方案。并且,正是因?yàn)?Ajax 基于 Java Script ,因此不可避免地帶來了 Java Script 的諸多缺點(diǎn),譬如:
????? - 跨瀏覽器是一場噩夢(mèng)
????? - 對(duì)搜索引擎的支持不好
????? - 干掉了 Back 、 History 等按鈕(盡管我并不認(rèn)為 Back 、 History 是什么好東西)
????? - 開發(fā)與維護(hù)成本過高
要 Java, 不要 Java Script
?We Love Java, Not Java Script 。套用毛澤東的慣用句式就是: “ 要 Java, 不要 Java Script” 。相信很多讀者看完這個(gè)標(biāo)題也許會(huì)不以為然,但這句話卻代表了許多 J2EE 開發(fā)人員的心聲。
眾多 Java 工程師都對(duì) Java 有一種近乎偏執(zhí)的喜愛,他們熱愛 Java 的簡潔與優(yōu)雅。但一旦讓他們?nèi)ミM(jìn)行 Java Script 的開發(fā),卻往往會(huì)不知所措:過度靈活的語法,無法通過編譯器進(jìn)行語法校驗(yàn),缺乏良好的調(diào)試工具等等這些,都會(huì)讓人們對(duì) Java Script 畏手畏腳,更遑論 Ajax 的開發(fā)。
一句話, Java 社區(qū)需要 Ajax ,需要它來提升基于 JavaEE 的 Web 應(yīng)用的人機(jī)體驗(yàn);但是,人們并不喜歡 Ajax 目前的開發(fā)模式" title="開發(fā)模式">開發(fā)模式。無疑,我們需要一種新的解決方案。
誰來拯救 JavaEE 的 Ajax ?
我給出的答案是 JSF 。目前,關(guān)于 JSF 的一種流行說法是“悲劇人生: Sun 讓 JSF 光著身子降臨到 Java Web 世界”。然而,我的看法卻是:作為一種革命性的服務(wù)器端組件技術(shù), JSF 猶如早晨八九點(diǎn)鐘的太陽,前途不可限量。
讓事實(shí)說話,我們先來看看 JSF 請(qǐng)求 / 響應(yīng)過程的標(biāo)準(zhǔn)生命周期:
????????????圖1 :JSF 的生命周期
??? 通過上圖可以觀察到,任何一個(gè) JSF“Faces Request” 請(qǐng)求,經(jīng)過 Restore View 、 Apply Request Values 、 Process Validations 、 Update Models 、 Invoke Application 等階段以后,產(chǎn)生了一個(gè) “Render Response” 返回給客戶端" title="客戶端">客戶端。那么,常規(guī) JSF 引擎是如何實(shí)現(xiàn)上述過程的呢?
??? ??
??? 回顧一下常規(guī) JSF 引擎針對(duì)請(qǐng)求與響應(yīng)的過程:首先,客戶端請(qǐng)求某個(gè)資源,產(chǎn)生一個(gè) Faces Request ;服務(wù)器端接收到此請(qǐng)求以后,經(jīng)過一系列后臺(tái)處理,產(chǎn)生一個(gè) Faces Response 。我們注意到:響應(yīng)的 Content-Type 是 text/html ,而產(chǎn)生的內(nèi)容主體是一段 HTML 文本;瀏覽器在接收到 HTML 文本以后,進(jìn)行整個(gè)頁面的渲染與刷新。
無需寫 Ajax 代碼的 Ajax Enabled 應(yīng)用
????? 我用自己開發(fā)的 JSF 引擎,這樣處理上述過程(詳見參考資料" title="參考資料">參考資料 www.OperaMasks.org ),如下圖所示:
?
???????? 圖 3 : OperaMasks JSF 實(shí)現(xiàn)的請(qǐng)求與響應(yīng)過程
???? 首先可以觀察到, Faces Request 的發(fā)出是基于 “x-requested-by: XML Http Request” ,也就是說,這是一個(gè) Ajax 請(qǐng)求,而該請(qǐng)求在到達(dá)服務(wù)器端以后,服務(wù)器端所產(chǎn)生的 Faces Response 同常規(guī) Faces Response 相比也發(fā)生了變化: Content-Type 不再是 text/html ,變成了 text/javascript ;并且,響應(yīng)的主體也不再是 html 文本,而是一堆 script 腳本。瀏覽器在接收到響應(yīng)以后,再也不需要進(jìn)行整個(gè)頁面的渲染與刷新,而只僅僅需要執(zhí)行這段腳本內(nèi)容,將頁面的控件進(jìn)行更新即可。
????? 顯而易見,通過上述 JSF 技術(shù),我們獲得了:
????? - 基于 Ajax 的請(qǐng)求、應(yīng)答、及頁面控件的更新
????? - 數(shù)據(jù)傳輸量明顯減少
????? - 避免整個(gè)頁面的刷新,更好的用戶體驗(yàn)
????? - 系統(tǒng)保持敏捷、高效
???? 換言之:任何標(biāo)準(zhǔn) JSF 應(yīng)用,只需將其在 OperaMasks JSF 引擎上運(yùn)行,就可以達(dá)到這樣的效果。我們并沒有寫任何一行 Ajax 的代碼,但是,我們的應(yīng)用卻是自然而然的 Ajax Enabled 的應(yīng)用。大道至簡,大象無形。
奧妙所在: JSF 的 Render 機(jī)制
為什么可以這樣?
JSF 組件只是特定狀態(tài)和行為的載體,而組件以什么形式去和用戶交互,是完全可定制的、獨(dú)立于該特定的表現(xiàn)語言,可以是 HTML 、 WML 或者其他形式;具體是什么,可以通過指定 JSF 組件的 Render Kit 來實(shí)現(xiàn),而每一種 Render Kit ,對(duì)應(yīng)于組件作者寫的同一風(fēng)格和形式的一系列 Render 。
比如,如果想在網(wǎng)頁中實(shí)現(xiàn)圖表功能( Chart) , MSIE 有 VML , Gecko 和 Opera 有 SVG ;而在服務(wù)器端只需要簡單地判斷一下瀏覽器類型,就可以選擇一個(gè) Render Kit ,生成不同的客戶端表現(xiàn)來完成相同功能――這是用常規(guī) JSP 技術(shù)很難完成的任務(wù)。
通俗的說, JSF 組件可以翻譯成任何你想要的形式。 So , JSF 框架比現(xiàn)有其它開源框架具有更強(qiáng)的生命力。上文所述的 OperaMasks JSF ,其容器級(jí)別 Ajax 實(shí)現(xiàn),正是靈活應(yīng)用 Render Kit 的具體案例。
從容器級(jí)別對(duì) Ajax 予以支持的 JSF 引擎
我們提出的 JSF 是直接由 JSF 容器來處理 Ajax 請(qǐng)求的,它會(huì)根據(jù)請(qǐng)求類型來判斷這是一個(gè)正常 HTTP 請(qǐng)求還是一個(gè) Ajax 請(qǐng)求:如果是常規(guī) HTTP 請(qǐng)求就運(yùn)行 JSP 頁面,生成頁面文檔(特定的,對(duì)于 Ajax Render kit ,要加入一些 Ajax 基礎(chǔ) JavaScript 代碼);如果是 Ajax 請(qǐng)求,服務(wù)器對(duì)請(qǐng)求參數(shù)正常解碼,并執(zhí)行 JSF 中除頁面輸出階段以外的所有其他階段,生成一個(gè) JSF 組件樹。
一直到這一步為止,處理方式與對(duì)普通 HTTP 請(qǐng)求的處理完全一致,唯一不同的是:在隨后 Render Response 階段,容器除了調(diào)用組件作者寫的 Ajax 功能 Renderer 以外,更重要的是在生成響應(yīng)頁面時(shí),會(huì)過濾掉一切不會(huì)變化的靜態(tài)內(nèi)容――也就是說,靜態(tài)內(nèi)容不會(huì)生成到響應(yīng)頁面中去,而對(duì)每一個(gè)動(dòng)態(tài)內(nèi)容則會(huì)生成一個(gè)相應(yīng) JavaScript 代碼(可以更進(jìn)一步優(yōu)化為只有變化了的動(dòng)態(tài)內(nèi)容才處理)。這樣,傳給客戶的 Ajax 應(yīng)答實(shí)際上是由這樣一些 JavaScript 語句構(gòu)成。在 Ajax 響應(yīng)返回到客戶端時(shí),就可以自動(dòng)由 Ajax 回調(diào)函數(shù)執(zhí)行這些 JavaScript 語句,完成對(duì)頁面即時(shí)的、局部的更改,而不需要刷新整個(gè)頁面。依賴 JSF 組件的具體功能,甚至可以改變頁面的外觀。而整個(gè) Ajax 機(jī)制由 JSF 引擎提供,對(duì)用戶完全透明。
實(shí)際上,在 JSF 規(guī)范中 JSF 頁面輸出階段所采用的 Render Kit 是可替換的,默認(rèn)的 HTML_BASIC Render Kit 輸出的是標(biāo)準(zhǔn) HTML 語法,不包含任何 Java Script 代碼。我們提出的 JSF 引擎實(shí)現(xiàn)了一個(gè) Ajax Render Kit ,可以在 HTML 文檔中嵌入 Java Script 代碼來實(shí)現(xiàn) Ajax 特性,而替換 Render Kit 只需要修改配置文件即可。
簡單地說,這種 JSF 引擎為每個(gè)標(biāo)準(zhǔn)組件都實(shí)現(xiàn)了相應(yīng)的 Ajax Render , 比如對(duì) UICommand 組件,其 Ajax Render 會(huì)在 onclick 事件中加入 JavaScript 的 Ajax 提交代碼,向服務(wù)器提交 Ajax 請(qǐng)求。通過這種方式,任何一個(gè)包含標(biāo)準(zhǔn) JSF 組件的 Web 應(yīng)用,都可以通過只更改 Render Kit 配置為 Ajax 來實(shí)現(xiàn) Web 應(yīng)用 Ajax 化。而對(duì)于第三方" title="第三方">第三方的組件,可能本身并不支持 Ajax ,但使用一個(gè)名為
例如, Apache myfaces 的 Tomahawk 項(xiàng)目提供了一個(gè) Tree 組件,這個(gè)組件本身并不支持 Ajax ,每當(dāng)按下一個(gè) Tree 結(jié)點(diǎn)都將重新刷新整個(gè)頁面。使用
綜上, JavaEE 需要 Ajax ,但并不需要傳統(tǒng)的 Ajax 開發(fā)模式。通過我們提出的 OperaMasks JSF 技術(shù),我們不再需要知道什么是 Ajax ,而我們的應(yīng)用卻是自然而然的 Ajax Enabled 應(yīng)用。
因此,我們認(rèn)為: JavaEE Without Ajax !