JSF的優點缺點及學習方法

先說JSF的優點,我覺得與其他Java前端框架相比,真正稱得上優點的就是一點:兼容幷包,體系開放。不少人覺得JSF難學,是因爲它一下子把太多東西攤在你面前。什麼組件化,視圖狀態,事件,backing bean,綁定,注入,Facelet模板,多語言,導航,校驗器轉換器,六個生命週期階段,EL求值,RenderKit,渲染器。看上去笨重得不得了,但事實上只要完全掌握了六個生命週期,就會發現其他東西都像是插件。JSF的API在設計上就已經考慮到了其中每一部分都是可替換的。比如說,你可以把EL Resolver換掉,換上Groovy的實現,或者支持任意一種JVM腳本語言的Backing Bean。也可以把RenderKit換掉,讓同一套組件渲染出不同的結果。或者把ViewHandler換掉,讓JSF可以直接開發Portlet。自定義模板或組件更是不在話下。喜歡不求甚解的人,就玩玩組件,拼拼頁面,也能出弄出個像模像樣的東西。喜歡刨根問底的人,只要把FacesServlet背後的東西搞清楚了,能把JSF變成任何自己想要的樣子。開放性帶來的另一個好處就是可測試性。Managed Bean本身是依賴注入的POJO,單元測試自然是沒問題的了。某些必須用FacesContext.getCurrentInstance()的場合,也可以通過在ThreadLocal裏放入Mock的方式來解決。JSFUnit更是豈一個爽字了得(就是在服務器端模擬了一個瀏覽器來做容器內測試)。

JSF的缺點,我覺得主要是兩個:

1. 性能問題。JSF是個規範,性能本來跟它沒有直接關係。但這種大而全又可擴展的東西本身就是鼓勵實現廠商拼了命的往裏加新特性。例如seam,第一天用它的@In來注入就被嚇了一跳,默認的注入時機居然是方法調用級別的。也就是說,如果一個bean上有10個@In,每當調用這個bean上任何一個public方法時,這些@In上的EL都被求值了一次(好處是被注入的值永遠都是最新的,不用擔心bean的scope問題)。自然,這種做法是不是會實際成爲性能瓶頸,要實際profiling才知道。但這些稀奇古怪的新特性,難保不會因爲算法之類的問題在某些場景下出現性能問題。所以個人覺得用JSF的話,一個好的profiling工具是必不可少的。

還有一個可能導致性能問題的地方是組件樹規模。在JSP裏,多用一個標籤只不過是在輸出流裏多了幾個字節。而在JSF裏,多一個組件就等於:更大的視圖狀態、按id查找組件時需要比較更多的組件、JSF生命週期中遍歷更多的組件(每一個週期階段都遍歷了一次組件樹)。特別是像include、naming container這類不可見的組件在組件樹中也算是一個實實在在的組件。而使用facelet更鼓勵用大量小組件來拼湊可複用的模板組件,一不小心,組件樹就會變得很龐大,有可能引起性能問題。但還是那句,性能問題必須用profiling來驗證。現在公司裏有些頁面就多達上千個組件,但profiling顯示瓶頸還是在數據庫處理上。

初學者如果不瞭解JSF生命週期和EL求值的順序,導致某些EL被重複求值,而這些EL裏又包含了耗時的操作,例如說訪問數據庫,也會引起性能問題。這個也可以通過profiling查出。

2. 不提供完備的概念封裝。這個聽起來有點玄,其實很淺白,就是看上去雖然JSF看上去大而全,一些書籍也在推銷JSF的封裝性有多麼的好。但事實上JSF所提供的概念體系,並不能對web開發所需要的基礎知識作完整的封裝和簡化。使用組件可以提高你的開發效率,但如果你不懂JavaScript,不懂CSS,僅僅在組件的層面上開發的話,客戶隨便一句“這裏我要圓角”就能讓你欲哭無淚。雖然JSF自動跨http請求保留組件的視圖狀態,但如果你不懂request和session的分別,你就會把所有backing bean都設爲session,引起性能問題,或者奇怪request bean裏爲什麼會拋空指針異常。雖然現在大部分JSF組件庫都自帶AJAX功能,如果你不懂AJAX,就會奇怪爲什麼我在A bean裏修改了B bean的東西,爲什麼頁面上B bean的部分沒有跟着變。在這點上,JSF跟Delphi之類的桌面應用開發環境是不同的。在Delphi的設計理念裏,你可以完全不懂數據庫,只用TTable和TDataSource與數據庫交互,也可以完全不懂OLE就直接用組件來操作Word。而在JSF中,雖然大部分時間你可能不需要使用這些基礎知識(所以開發速度快),但如果一旦需求超出了JSF封裝的概念體系,而你又不懂基礎的東西,在這裏卡住的時間會讓你把之前省下來的時間都耗進去。不過,話又說回來,目前爲止還沒哪個開發框架可以對web開發在概念上完整封裝的。做得最好的可能是.net,現在還是回過頭來搞MVC了。.net也覺得只用組件太封閉了,現在再開放一些MVC的特性讓你能處理一些底層的需求。而JSF從一開始在各個層次上就都是開放的(基礎到本身就是按MVC來架構的,可以跟Servlet和JSP協作,或者通過FacesContext拿到HttpServletResponse),而且從來就沒有試圖把程序員侷限在某一個抽象層面上。

我學習JSF的步驟:

1. 使用組件是入門,如果要用好JSF,至少要搞清楚六個生命週期和它們的執行順序。例如:首次請求和postback時生命週期有什麼不同;每個生命週期中遍歷組件樹都幹了什麼; 校驗或轉換出錯了生命週期的執行有什麼不同;immediate屬性對事件組件自身和其他輸入組件的求值有什麼影響;FacesContext.renderResponse()和FacesContext.responseComplete()對生命週期執行的影響(包括phase listener)等等。

2. 一定要找齊框架的源碼。其實跟蹤一下就會發現,JSF沒有想象中那麼高深莫測。千萬不要debug時跟蹤到沒有代碼的框架類,就束手無策,開始大罵框架難用,還不如用jsp,至少源碼是自己的。

附找源碼的方法:關鍵是確認當前服務器所用jar包的版本,在managed bean中向控制檯輸出javax.faces.webapp.FacesServlet.class.getProtectionDomain().getCodeSource().getLocation().toString(),可以知道服務器所用的jsf-api.jar位置。任何沒有源碼的類都可以先用這個方法找出jar包位置。用解壓縮工具打開.jar包,打開在META-INF中的MANIFEST.MF文件,裏面通常會有打包時的版本信息,或者打包日期。上網找這個版本的源碼,或者到代碼庫中checkout這個版本的tag,或者checkout這個日期的snapshot。

3. 閱讀示例或開源項目的源碼。richfaces的代碼庫就能checkout到不少示例。

4. 真正用JSF做應用的時候,應該用好facelet,到這裏才體現出JSF相對於JSP的優勢。不過模板引用多了可能不太好理解(主要是對於新加入項目的人),我個人喜歡用graphviz (www.graphviz.org)來畫關係圖,隨便用個腳本語言(我用了scala)或者Java遍歷xhtml文檔生成dot文件就行了。facelet除了模板標籤庫(ui標籤庫)外,還提供了自定義模板組件,函數和Tag Handler的功能。

5. JSF只是個規範,不同廠商都會提供一些額外的特性。要好好理解和利用這些特性,增強的組件庫只是其中一點。通常不同的廠商在注入、綁定和EL上都會有一些改進。

6. 自己動手寫應用。

7. 入門之後,有空就可以研究一下怎樣做JSF的組件和渲染器了。標準JSF的自定義組件方案還是比較複雜的,不過廠商一般都會有自己的簡化方案,以便新組件能融入原來的體系。對於項目來說,自定義組件不是必須的,但如果熟悉了自定義組件和渲染器,就基本上能在JSF裏爲所欲爲了。

8. 非常有空的時候,老老實實從頭到尾看一遍JSF規範(www.jcp.org)和API的javadoc。

發佈了36 篇原創文章 · 獲贊 20 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章