天天看點

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裡為所欲為了。