目前應用很廣泛而且也很成熟的Struts應用開發框架,在未來幾年裡將會逐漸淡出,其基於Action(action-based)的開發模型也將被活躍的java社區所推崇的新的開發模型所替代,對於java開源社區來說,追求完美是很多人的理想。在2000年初誕生的Struts以及其他類似的 MVC(Model VIEw Controler)框架主要都是以操作為中心,且都是無狀態的開發模型,而現在,基於組件(component-based)和事件驅動(event- driven)的開發模型逐漸流行起來,在很多時候都成了Struts的有利競爭者,這其中來自jcp的JSr127-JavaServer Faces(JSF)以及來自apache的Tapestry是其中的佼佼者。
在這篇文章中,我們將把這兩種框架JSF和Tapestry進行詳細的對比。我們的比較將涉及到兩種框架的設計、運行環境以及如何開發。比較的目的在於讓讀者了解這兩種框架各自的優缺點,以便於在自己的項目中,根據實際情況,選擇合適的框架。該文章的內容基於JSF1.1和Tapestry3.0.3 (在個別地方由於需要會談到Tapestry4.0)。
JSF和Tapestry簡介
JSF源於JCP(Java Community Process)的JSR127規范。Sun公司自己同時也對該規范提供了一個實現,目前的版本是1.1,而且該項目的設計者之一正是Struts的作者 Craig McClanahan。另外一個JSF的實現,就是apache的一個項目MyFaces。目前,幾個主流的java開發工具廠商都在其Java ide中提供了對jsf的支持,比如Sun、Oracle、IBM、Borland等,而且JSF的後續版本JSF1.2也將成為J2EE5.0的一個組成部分。
Tapestry最初於2000年在SourceForge注冊,來自於Apple WebObjects,隨後,於2003年成為apache的一個項目。與JSF不同的是,Tapestry並不是某一個JSr規范的實現,它僅僅是一個開源項目,當前版本是3.0.3。4.0版本預計也將在後面的一段時間內推出。
JSF和Tapestry作為MVC框架,在一些基本特性上是非常類似的:
◆它們都讓開發者不再直接與Servlet API打交道,而是讓開發者在一個更高的抽象層上思考問題;
◆它們都將web頁面上的顯示元素綁定到一個java對象的某個屬性上,這些屬性可能是字符串、數字、日期或者其他類型,並且由該對象來維護其狀態。用戶在頁面上的交互行為(比如用鼠標點擊一個按鈕或者鏈接)都直接映射為Java類中的一個事件處理方法;
◆兩種框架都支持組件式的開發方式,並且開發的組件可被其他開發者重用。兩種框架都自帶一個標准組件庫,提供web開發的常見通用功能。
在下面的章節裡面,我們將會看見這兩個框架在各自的實現方式上是有很大區別的。對於大多數程序員來說,基於兩種框架做開發,將是非常不同的兩種體驗。
Sidebar: 例子程序在下面本文提供了一個例子程序,該程序的代碼大部分都將在這片文章中做出詳細的描述。這個程序主要就是一個管理個人假期的工具,它的主要功能包括:
◆一個home界面, 列出所有登記的假期,包括假期開始時間、天數以及一下描述信息;
◆一個detail界面,用於浏覽某一登記假期的詳細信息;
◆一個new界面,用於添加一個假期信息。
下面這幅圖展示了該程序的主體流程和功能:
對比1: 頁面開發(Page Development)
一個web應用實際上就是後端用Java代碼獲取相應數據,並將數據傳遞給前端表現層代碼,然後最終返回給終端用戶。因此,在一個開發人員看來,當他初次接觸JSF和Tapestry時,最直接的感覺就是JSF的表現層是基於JSP的模板技術,而Ta pestry的表現層模板基本就可以看作是一個Html。
JSF
JSF采用JSP的技術作為其表現層技術。與標准兼容的JSF實現必須實現一個核心組件的JSP標簽集。含有JSF標簽的Html頁面不能在標准浏覽器中預覽。要想浏覽,必須使用JSF設計工具或者直接部署到應用中,在真實運行環境中浏覽。
對於JSF技術來說,其表現層技術就是JSP,但是這不是唯一的解決方法。Hans Bergsten的文章介紹了可供選擇的其他的方法,讀者可閱讀該文章獲取詳細信息,Improving JSF by Dumping JSP 。 Bergsten的文章中指出了混合JSF和JSP標簽暴露出的一些問題。當然,這些問題在JSF1.2和JSP2.1中會得到解決。
Tapestry
對於大多數的Tapestry應用來說,Tapestry的表現層模板看起來就是一個簡單的規則的Html,只不過其中加入了一些Tapestry的屬性。
大家可以嘗試把代碼放入一個Html的body標簽當中,你會發現浏覽器可以正常的觀看它。
上面的例子中,jwcid = "@componentName"屬性就是定義了一個Tapestry標簽。
Tapestry的模板不僅僅可以用HTML作為載體,它也支持其他的一些標記語言,Tapestry標簽是具有良好格式的標簽,即必須成對出現。 Tapestry模板技術支持的標記語言典型的就是Html以及用於無線應用的WML。其最大的一個特點 就是,可脫離Servlet容器,直接預覽。
事實上JSF標簽由於不是標准的Html標簽,使得它對於初學者來說,是難於使用的。而對於很多Java程序員來說,他們喜歡編輯HTML代碼,至少是樂意編寫Html代碼。
JSF技術宣稱的一個技術優點就是,可使用同一個模板編寫運行在不同設備上的應用,由此帶來很大的靈活性。然而,這樣做,由於要協調不同設備間的表現差異性,那麼很可能同一個模板,將不能正好表現你的輸出。同時,你不得不學習新的標簽庫的使用方法,並且搞懂它們是怎樣映射到Html的。隨著時間的推移, JSF標簽的簡潔可幫助你很快的編寫表現層代碼,同時也降低了開發者對JSf設計工具的依賴。
本文認為,JSF的學習成本高於其技術優點。在大多數情況下,我們並不需要編寫適應於不同設備的應用。盡管JSF設計工具提供了簡單的圖形化的方法來構建和預覽JSF應用,但是在一個開發中,頁面設計人員更多的是喜歡用流行的Html設計工具來編寫和預覽頁面,這就發生了一個沖突,即只有將更多的頁面工作轉移到Java程序員身上,因為一個頁面設計人員通常情況下是不樂意去操作JSF設計工具的。
JSF開發者一直在尋找一種解決這些問題的方法。JSF技術設計良好的擴展體系,使得這成為可能,其中一個技術浮現出來,那就是表現層控制器。一個非常有前途的表現層控制器的實現就是Facelets ,由Java.Net創建的開放源代碼項目。Facelets的靈感就來源於Tapestry的模板模型,這使得JSF不再依賴於JSP技術。Facelets允許開發者創建Tapestry風格的標簽。
在未來的一段時間內,Facelets將會被開發者所采用,或許會影響JSF未來的版本。和JSF比較,Tapestry在表現層方面基於Html代碼,可被標准浏覽器所浏覽,這正是它在表現層上的優勢所在。
比較2: java編程模型(Java Programming Model)
在前面我們提到Tapestry和JSF都允許表現層的模板直接和一個Java類中的屬性和方法進行交互,那這些類的實例在運行時是怎麼創建和管理的呢?
Tapestry
Tapestry的一個完整過程通常都要包括三個部分:用於顯示的頁面模板(一般就是Html),帶有相關屬性和方法的java類,用於定義頁面模板上的控制元素和Java類的關系的頁面定義文件。Tapestry有著一套特殊的訪問HttpSess ion、ServletContext的體系。
在一個頁面定義文件中描述所有的數據綁定是可以的,但是一個頁面控制元素卻不能在request周期內,綁定一個Java類。在request周期內只能通過一個page類訪問一些屬性和方法。這其中最主要的限制就是關於page類的問題,一個pag e類必須是BasePage或者AbstractPage的派生類。從另一方面來說,這就意味著你所編寫的表現層邏輯的代碼將會和框架本身的實現有著非常緊密的耦合。不過,Tapestry未來的版本就會減少這種耦合,努力成為一個松耦合的編程模型。
JSF
在JSF應用中沒有頁面定義文件。它只有一個全局的配置文件,命名為:faces-config.xml,裡面通常都定義了一堆"managed beans"。這些managed beans都是帶有屬性和事件監聽器的定義良好的java bean。在faces-config.XML中定義的後端bean都有三個參數:一個標識符、一個Java類名、一個bean的生存周期,生存周期可以是request、session、application中的一種。一旦在 faces-config.XML中定義好了一個managed bean,那一個前端頁面上的顯示控制元素就可以使用標識符來關聯這個bean。managed bean也可以配置為引用另外一個managed bean。
JSF和Tapestry都可以方便的與其他的中間層技術整合,比如Spring。JSF managed bean facility 是一個IoC(Inversion of Control)。通過諸如 JSF-Spring 這樣的擴展技術,我們可以方便將其和Spring很好的整合在一起,使得JSF的表達式可以調用Spring的bean的方法。雖然Spring可以與 Tapestry3.0整合,但是在Tapestry4.0當中才能更好的充分發揮IoC特性;Tape stry的領導Howard Lewis Ship已經在Tapestry啟動了IoC框架的工作。Spring的bean將會很輕松的注入到Tapestry應用的類中。
JSF的編程模型提供了更大的靈活型,因為你可以通過組合的方式來豐富你的代碼功能。比如,你可以設定一個指定的managed bean完成頁面的某些功能,同時,你也可以在這些bean裡引用其他的managed bena,從而可在頁面間共享一些功能。
而Tapestry必須使用類繼承的方法,也就是說,Tapestry應用中的一個頁面類必須從框架指定的基類中派生(包含一大堆框架指定的狀態),這並不是一個理想的方法。
JSF能夠非常直觀的管理session和application周期內的狀態: 頁面代碼可以方便的訪問managed bean,而不管其生命周期是request、session還是application。而Tapestry在這方面就相對差點,不過, Tapestry4.0在這方面做了很大的改進,引入了類似於JSF managed bean的技術,同時,還支持Java 5.0的annotaions技術,減少對XML配置文件的依賴,從而降低應用配置的復雜性。
JSF在編程模型上來看,與Tapestry相比具有更大的靈活性。
對比3:請求處理生命周期(Request Processing Life Cycle)
請求處理生命周期在一個web應用中是很重要的,它反映了一個請求從提交到將信息返回給客戶端的處理過程。當然,請求處理生命周期必須能以某種優雅的方式,在正確的地方插入用戶定制的請求處理操縱邏輯。
JSF
JSF的請求處理生命周期清晰的定義成六個步驟:Restore VIEw、Apply Request Values、 Process Validations、 Update Model Values、 Invoke Application 和 Render Response。從第二步Apply Request Values開始,可以直接跳到最後一步Render Response,甚至可以直接返回給客戶端並且通知JSF運行時,響應操作已經完成了。有些方法要訪問JSF的FacesContext對象,比如狀態監聽器(phase listeners)、事件操縱器(event handlers)、轉換器(converters)、驗證器(validators)等,這都可能忽略請求處理生命周期中的某些步驟。
Tapestry
JSF有一個單一的生命周期模型,而Tapestry的生命周期依賴於其調用的引擎服務(engine service)。每一個引擎服務(engine service)都有其自己的生命周期。比如,Tapestry中的Direct Service控制標單的提交,而Page Service用於渲染頁面,並且不需要額外的服務端操作。每一個引擎服務(engine service)都被設計成在自身的生命周期內完成一些特定的任務。這就意味著,對於一個特定的需求,你可以創建一個對應的引擎服務(engine service),並且可自己定制生命周期。
JSF的生命周期概念更容易理解,而Tapestry可對一個特定的操作定義一個生命周期,這在某些問題的解決上,可能會提供更優雅的解決方案。