JSF和Struts的區別概述

據說JSF的主要負責人就是struts的主要作者,所以二者的相似點還是有很多的。
  • 都採用taglib來處理表示層:在jsp頁面中,二者都是採用一套標記庫來處理頁面的表示和model層的交互。
  • 二者都採用了bean來作爲和jsp頁面對應的model層。該model層保存了jsp頁面上的數據,同時可以作一些驗證工作,在struts中就是FormBean,在JSF中就是back bean。
  • 都採用bean作爲控制層,Struts中採用ActionBean來處理業務邏輯,對於簡單的應用可以直接在ActionBean中編寫業務邏輯代碼,也可以調用另外的bean或者EJB來處理業務邏輯;對於JSF則採用backing bean來處理業務邏輯,同樣,backing bean也可以直接編寫業務邏輯或者調用其他的bean來處理業務邏輯。
  • 都採用xml配置文件來處理bean的配置,頁面導航等問題,增加了系統的靈活性。
  • 都採用資源文件來處理國際化和本地化的問題。
然而,二者的不同點也很多,下面分別說明:
  1. 首先二者的側重點不同,Struts側重於控制層,側重於如何分派和處理用戶的請求,所以表示層的taglib功能不夠強大。而JSF則側重於表示層,實現了大量的標準組件,允許開發人員對表示層有更多的控制權,同時JSF實現了一個開放的架構,允許開發人員創建自己的組件,或者在現有的組件上繼承,開發功能更強大的組件。本人認爲這是JSF最大的一個特色。(有點類似於vcl和.net組件)
  2. 和jsp 對應的model層,在Struts中採用FormBean來保存用戶輸入的數據,基本上一般字段的類型都是String。而且可以進行簡單的驗證,當然如果採用動態的FormBean就不能在FormBean中進行驗證了。在Struts中,jsp和FormBean是緊密結合在一起的,只要寫一個 jsp就必須對應一個FormBean,同時jsp上的每個組件都對應FormBean中相同名字的字段。本人認爲這裏不太靈活,比如,開發頁面的時候就必須考慮後臺的FormBean的實現,但此時如果該頁面沒有FormBean的化則程序運行時會出錯。在JSF中,JSP頁面中的組件通過value屬性和backing bean的字段關聯,這樣就有比較大的靈活性,頁面上的每個組件可以對應相同的backing bean,也可以對應不同的backing bean(當然本人認爲在一般的應用中,一個頁面上的組件還是都對應到一個backing bean較好),而且在設計頁面的時候可以不考慮backing bean如何設計,可以在設計完頁面之後再考慮backing bean的實現問題。
  3. 關於數據驗證,Struts可以採用在FormBean中的驗證函數中進行驗證,也可以使用validator進行驗證(關於這種驗證方法,本人沒有測試過,不知效果如何,希望有經驗的朋友指教!)。在JSF中,提供了一些標準的validator。可以對輸入的數據做一些簡單的驗證,例如驗證數值數據的範圍,字段是否必填等。但其驗證的反饋信息爲英文。如果該信息不能自定義的化,那麼針對國內的應用就不太適合了,目前本人還沒有找到該反饋信息是否能夠自定義的辦法。另外對於input類型的組件可以通過validator屬性關聯到backing bean的一個驗證方法上。在事件處理方法中進行驗證也是一個辦法。
    在JSF中還有一個問題就是在JSF生成的頁面中,組件的Id命名比較怪異,所有的組件的id都類似於“form:compnentid”即form的名稱+“:” +組件的id。這樣通過javascript訪問組件就不是很方便,通過form.id形式好像不能訪問到組件。不知道各位有沒有好的解決方案。                                                                         
  4. 控制層:Struts 中通過form的action來提交請求,通過ActionServlet來分發請求,最後由ActionBean來處理請求,在Action中實現業務邏輯或者調用其他的業務邏輯bean來完成用戶的請求並返回客戶端。在這裏,一個form只有一個action,即一個頁面只能提交到一個action Bean。對於頁面上有多個按鈕都需要提交的情況就需要使用一些變通的方法了。和傳統的web開發的模式比較接近。
    對於JSF,採用了事件模式來處理用戶提交的請求。JSF實現了事件監聽器來監測事件,例如當用戶單擊了一個按鈕就會觸發一個按鈕單擊事件,還有valuechange事件監聽器來監測數值改變的事件等。例如在頁面中通過通過CommandButton按鈕的action屬性來關聯到backing bean的方法來執行相應的操作。每個不同的按鈕都可以關聯不同的方法,當然也可以關聯相同的方法(這樣就和Action Bean非常類似了)。這中開發模式比較接近於傳統的c/s模式或者Asp.net的開發模式。對於那些從c/s架構程序或者Asp.net架構轉過來的開發者來說,這種方式可能更自然一些。
    在JSF的一些簡單的示例程序中,通常把和jsp對應的model層和jsp所提交的action放在同一個backing bean中,即業務邏輯和業務邏輯所處理的數據在同一個bean中。本人認爲,這樣的結構只能用在簡單的應用中,對於企業級的開發並不適合。應該將頁面所關聯的數據和頁面所做的action分開,這樣的結構更好一些,比較類似於struts的結構。
    JSF的backing bean中的方法訪問session,request等沒有struts中的直觀。筆者找了很多例子才知道如何訪問session中的數據。
  5. 頁面的導航:關於頁面的導航,struts和JSF比較類似。都是在xml的配置文件中配置導航規則。每個要跳轉的頁面都有一個別名,在程序中通過別名進行跳轉。另外Struts中的跳轉是在ActionBean中發生,execute方法最後返回一個actionForward來進行跳轉。而JSF則在事件處理方法中最後返回一個字符串,由系統在xml文件中匹配自動進行跳轉。在JSF中也可以通過在JSP頁面的CommandButton的action 屬性中直接填寫跳轉的別名直接跳轉,而不必經過事件處理方法的處理。
  6. 資源文件的管理:Struts和JSF對於資源文件的管理比較類似,Struts中在struts-config.xml中對資源文件進行配置,實現整個程序的統一管理。而對於JSF則可以在每個JSP頁面中分別定義資源文件,然後通過資源文件的別名來訪問資源文件中的內容。兩者的格式也不相同,在 Struts中,格式爲: grade1.grade2.grade3 = your information,通過“.”來表示級別。而在JSF中則必須通過下劃線來表示級別,例如grade1_grade2_grade3= your information。本人認爲還是struts的方案更直觀一些。另外在Struts的資源文件中可以定義信息的顯示格式,例如: error.header,error.footer。而JSF中如何定義還不太清楚,或者可以通過定義Messages標記的屬性來定義。 
Struts和JSF/Tapestry都屬於表現層框架,這兩種分屬不同性質的框架,後者是一種事件驅動型的組件模型,而Struts只是單純的MVC模式框架,老外總是急吼吼說事件驅動型就比MVC模式框架好,何以見得,我們下面進行詳細分析比較一下到底是怎麼回事?
  首先事件是指從客戶端頁面(瀏覽器)由用戶操作觸發的事件,Struts使用Action來接受瀏覽器表單提交的事件,這裏使用了Command模式,每個繼承Action的子類都必須實現一個方法execute。
  在struts中,實際是一個表單Form對應一個Action類(或DispatchAction),換一句話說:在Struts中實際是一個表單只能對應一個事件,struts這種事件方式稱爲application event,application event和component event相比是一種粗粒度的事件。
  struts重要的表單對象ActionForm是一種對象,它代表了一種應用,這個對象中至少包含幾個字段,這些字段是Jsp頁面表單中的input字段,因爲一個表單對應一個事件,所以,當我們需要將事件粒度細化到表單中這些字段時,也就是說,一個字段對應一個事件時,單純使用Struts就不太可能,當然通過結合JavaScript也是可以轉彎實現的。
  而這種情況使用JSF就可以方便實現,
<h:inputText id="userId" value="#{login.userId}">
  <f:valueChangeListener type="logindemo.UserLoginChanged" />
</h:inputText>
  #{login.userId}表示從名爲login的JavaBean的getUserId獲得的結果,這個功能使用struts也可以實現,name="login" property="userId"
  關鍵是第二行,這裏表示如果userId的值改變並且確定提交後,將觸發調用類UserLoginChanged的processValueChanged(...)方法。
  JSF可以爲組件提供兩種事件:Value Changed和 Action. 前者我們已經在上節見識過用處,後者就相當於struts中表單提交Action機制,它的JSF寫法如下:
<h:commandButton id="login" commandName="login">
  <f:actionListener type=”logindemo.LoginActionListener” />
</h:commandButton>
  從代碼可以看出,這兩種事件是通過Listerner這樣觀察者模式貼在具體組件字段上的,而Struts此類事件是原始的一種表單提交Submit觸發機制。如果說前者比較語言化(編程語言習慣做法類似Swing編程);後者是屬於WEB化,因爲它是來自Html表單,如果你起步是從Perl/PHP開始,反而容易接受Struts這種風格。
基本配置
  Struts和JSF都是一種框架,JSF必須需要兩種包JSF核心包、JSTL包(標籤庫),此外,JSF還將使用到Apache項目的一些commons包,這些Apache包只要部署在你的服務器中既可。
  JSF包下載地址:[url]http://java.sun.com/j2ee/javaserverfaces/download.html[/url]選擇其中Reference Implementation。
  所以,從JSF的驅動包組成看,其開源基因也佔據很大的比重,JSF是一個SUN夥伴們工業標準和開源之間的一個混血兒。
  上述兩個地址下載的jar合併在一起就是JSF所需要的全部驅動包了。與Struts的驅動包一樣,這些驅動包必須位於Web項目的WEB-INF/lib,和Struts一樣的是也必須在web.xml中有如下配置:
<web-app>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.faces</url-pattern>
  </servlet-mapping>
</web-app>
  這裏和Struts的web.xml配置何其相似,簡直一模一樣。
  正如Struts的struts-config.xml一樣,JSF也有類似的faces-config.xml配置文件:
<faces-config>
  <navigation-rule>
    <from-view-id>/index.jsp</from-view-id>
    <navigation-case>
      <from-outcome>login</from-outcome>
      <to-view-id>/welcome.jsp</to-view-id>
    </navigation-case>
  </navigation-rule>
  <managed-bean>
    <managed-bean-name>user</managed-bean-name>
    <managed-bean-class>com.corejsf.UserBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>
</faces-config>
 
  在Struts-config.xml中有ActionForm Action以及Jsp之間的流程關係,在faces-config.xml中,也有這樣的流程,我們具體解釋一下Navigation:
  在index.jsp中有一個事件:
<h:commandButton label="Login" action="login" />
  action的值必須匹配form-outcome值,上述Navigation配置表示:如果在index.jsp中有一個login事件,那麼事件觸發後下一個頁面將是welcome.jsp
  JSF有一個獨立的事件發生和頁面導航的流程安排,這個思路比struts要非常清晰。
  managed-bean類似Struts的ActionForm,正如可以在struts-config.xml中定義ActionForm的scope一樣,這裏也定義了managed-bean的scope爲session。
  但是如果你只以爲JSF的managed-bean就這點功能就錯了,JSF融入了新的Ioc模式/依賴性注射等技術。
Ioc模式
  對於Userbean這樣一個managed-bean,其代碼如下:
public class UserBean {
  private String name;
  private String password;
  // PROPERTY: name
  public String getName() { return name; }
  public void setName(String newValue) { name = newValue; }
  // PROPERTY: password
  public String getPassword() { return password; }
  public void setPassword(String newValue) { password = newValue; }
}
<managed-bean>
  <managed-bean-name>user</managed-bean-name>
  <managed-bean-class>com.corejsf.UserBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
  <managed-property>
    <property-name>name</property-name>
    <value>me</value>
  </managed-property>
  <managed-property>
    <property-name>password</property-name>
    <value>secret</value>
  </managed-property>
</managed-bean>
  faces-config.xml這段配置其實是將"me"賦值給name,將secret賦值給password,這是採取Ioc模式中的Setter注射方式
Backing Beans
  對於一個web form,我們可以使用一個bean包含其涉及的所有組件,這個bean就稱爲Backing Bean, Backing Bean的優點是:一個單個類可以封裝相關一系列功能的數據和邏輯。
  說白了,就是一個Javabean裏包含其他Javabean,互相調用,屬於Facade模式或Adapter模式。
  對於一個Backing Beans來說,其中包含了幾個managed-bean,managed-bean一定是有scope的,那麼這其中的幾個managed-beans如何配置它們的scope呢?
<managed-bean>
  ...
  <managed-property>
    <property-name>visit</property-name>
    <value>#{sessionScope.visit}</value>
  </managed-property>
  這裏配置了一個Backing Beans中有一個setVisit方法,將這個visit賦值爲session中的visit,這樣以後在程序中我們只管訪問visit對象,從中獲取我們希望的數據(如用戶登陸註冊信息),而visit是保存在session還是application或request只需要配置既可。
UI界面
  JSF和Struts一樣,除了JavaBeans類之外,還有頁面表現元素,都是是使用標籤完成的,Struts也提供了struts-faces.tld標籤庫向JSF過渡。
  使用Struts標籤庫編程複雜頁面時,一個最大問題是會大量使用logic標籤,這個logic如同if語句,一旦寫起來,搞的JSP頁面象俄羅斯方塊一樣,但是使用JSF標籤就簡潔優美:
<jia:navigatorItem name="inbox" label="InBox"
  icon="/p_w_picpaths/inbox.gif"
  action="inbox"
  disabled="#{!authenticationBean.inboxAuthorized}"/>
  如果authenticationBean中inboxAuthorized返回是假,那麼這一行標籤就不用顯示,多幹淨利索!
  先寫到這裏,我會繼續對JSF深入比較下去,如果研究過Jdon框架的人,可能會發現,Jdon框架的jdonframework.xml中service配置和managed-bean一樣都使用了依賴注射,看來對Javabean的依賴注射已經迅速地成爲一種新技術象徵,如果你還不瞭解Ioc模式,趕緊補課。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章