Struts1與Struts2的比較

Struts 作爲 MVC 2的 Web 框架,自推出以來不斷受到開發者的追捧,得到廣泛的應用。作爲最成功的 Web 框架,Struts 自然擁有衆多的優點:MVC 2模型的使用、功能齊全的標誌庫(Tag Library)、開放源代碼。

但是,正所謂“沒有最好,只有更好”,Struts1.x 自身也有不少的缺點:需要編寫的代碼過多,容易引起“類爆炸”、單元測試困難。這些缺點隨着 Web 的發展越來越明顯。這就促生了 Struts 2,它的誕生能很好的解決上述問題。

一、引言

Struts 的第一個版本是在2001年5月份發佈的。它的最初設想是通過結合 JSP 和 Servlet,使 Web 應用的視圖和業務/應用邏輯得以清晰地分離開來。在 Struts 之前,最常見的做法是在 JSP 中加入業務和應用邏輯,或者在 Servlet 中通過 println()來生成視圖。

自從第一版發佈以來,Struts 實際上已成爲業界公認的 Web 應用標準。它的炙手可熱也爲自己帶來了改進和變更,所以不但要跟上對 Web 應用框架不斷變化的需求,而且要與日漸增多競爭激烈的衆多框架的特性相融合。

到最後,產生了幾個下一代 Struts 的解決方案。其中兩個最受矚目的方案是 Shale 和 Struts Ti。Shale 是一個基於構件的框架,並在最近成爲 Apache 的頂級項目。而 Struts Ti 則是在 Struts 的成功經驗基礎上繼續堅持對前端控制器(Front Controller)和MVC(model-view-controller)模式進行改進。 

WebWork 項目是在2002年3月發佈的,它對 Struts 式框架進行了革命性改進,引進了不少新的思想、概念和功能,但和原 Struts 代碼並不兼容。WebWork 是一個成熟的框架,經過了好幾次重大的改進與發佈。 

在2005年12月,WebWork 與 Struts Ti 宣佈合併。與此同時,Struts Ti 改名爲 Struts Action Framework 2.0,成爲 Struts 真正的繼承者。 

最後要注意的是,並不是說 Struts 或 WebWork 項目已經停止開發了。由於人們對這兩個項目的興趣仍然很高,而且也有很多開發者仍然願意使用它們,因此這兩個項目還在繼續開發中,繼續修復 Bug,改進功能和繼續添加新功能。 

二、 Action 的區別 

對於有着豐富的 Struts1.x 開發經驗的朋友來說,都十分的清楚 Action 是整個 Struts 框架的核心內容,當然 Struts2也不例外。不過,Struts1.x 與 Struts2的 Action 模型很大的區別。 

Struts2和 Struts1.x 的差別,最明顯的就是 Struts2是一個 pull-MVC 架構。這是什麼意思呢?從開發者角度看,就是說需要顯示給用戶的數據可以直接從 Action 中獲取,而不像 Struts1.x 那樣,必須把相應的 Bean 存到 Page、Request 或者 Session 中才能獲取。Struts1.x 必須繼承org.apache.struts.action.Action 或者其子類,表單數據封裝在 FormBean 中。Struts 2無須繼承任何類型或實現任何接口,表單數據包含在 Action 中,通過 Getter 和 Setter 獲取(如下面的 ActionForStruts2的代碼示例)。

雖然,在理論上 Struts2的 Action 無須實現任何接口或者是繼承任何的類,但是,在實際編程過程中,爲了更加方便的實現 Action,大多數情況下都會繼承 com.opensymphony.xwork2.ActionSupport 類,並且重載(Override)此類裏的 String execute()方法。如下所示:

package ActionDiffer;
import
 java.text.DateFormat;
import
 java.util.Date;
import
 com.opensymphony.xwork2.ActionSupport;
public class ActionForStruts2 extends
 ActionSupport {
private
 String message;
public
 String getMessage() {
return
 message;
}
@Override
public
 String execute() {
message 
= " This is hello from strtuts2. Now is: " +

            DateFormat.getInstance().format( 
new Date());
return
 SUCCESS;
}
}

首先,從 ActionForStruts2可以看出,返回的對象不是 ActionForward,而是 String。如果你不喜歡以字符串的形式出現在你的代碼中,有個 Helper 接口 Action 可以以常量方式提供常見結果,如“success”、“none”、“error”、“input”和“login”。 

另外, 按照慣例,在 Struts1.x 中只有“execute”方法能調用 Action, 但在 Struts2中並非必要,任何聲明爲 public String methodName() 方法,都能通過配置來調用 Action。 

最後,和 Struts1.x 最大的革命性的不同是,Struts2處理 Action 過程中調用的方法(“execute”方法)是不帶參數的。那如何獲取所需要的對象呢?答案是使用 IoC(反轉控制,Inversion of Control),也叫“依賴注入(Dependency Injection)”的模式(想更多地瞭解這方面信息請看Martin Fowler的文章http://www.martinfowler.com/articles/injection.html)。Spring框架使得這個模式流行起來,然而 Struts2的前身(WebWork)也同時應用上了這個模式。 

三、IoC 

IoC(Inversion of Control,以下譯爲控制反轉),隨着 Java 社區中輕量級容器(Lightweight Contianer)的推廣而越來越爲大家耳熟能詳。在此,無需再多費脣舌來解釋“什麼是控制反轉”和“爲什麼需要控制反轉”。因爲互聯網上已經有非常多 的文章對諸如此類的問題作了精彩而準確的回答。讀者可以去讀一下 Rod Johnson 和 Juergen Hoeller 合著的《Expert one-on-one J2EE Development without EJB》或Martin Fowler所寫的《Inversion of Control Containers and the Dependency Injection pattern》。

衆所周知,Struts2是以 Webwork 2作爲基礎發展出來。而在 Webwork 2.2之前的 Webwork 版本,其自身有一套控制反轉的實現,Webwork 2.2在 Spring 框架的如火如荼發展的背景下,決定放棄控制反轉功能的開發,轉由 Spring 實現。值得一提的是,Spring 確實是一個值得學習的框架,因爲有越來越多的開源組件(如 iBATIS 等)都放棄與 Spring 重疊的功能的開發。因此,Struts2推薦大家通過 Spring 實現控制反轉。  

爲了更好地瞭解反轉控制,下面來看一個例子,如何利用 IoC 在 Action 處理過程中可以訪問到當前請求 HttpServerRequest 對象。 

在例子中,使用的依賴注入機制是接口注入。就如其名稱一樣,接口注入需要的是已經被實現了的接口。這個接口包含了相應屬性的 setter,爲 Action 提供值。例子中使用了 ServletRequestAware 接口,如下:

public interface ServletRequestAware {
public void
 setServletRequest(HttpServletRequest request);
}

當繼承這個接口後,原本簡單的 Action 看起來有點複雜了,但是這時可以獲取 HttpServerRequest 對象來使用了。

public class IoCForStruts2 implements ServletRequestAware {
private
 HttpServletRequest request;
public void
 setServletRequest(HttpServletRequest request) {
this.request =
 request;
}
public String execute() throws
 Exception {
// 可以開始使用request對象進行工作了

            return Action.SUCCESS;
}
}

看起來現在這些屬性是類級別的,並不是線程安全的,會出現問題。其實在 Struts2裏並沒有問題,因爲每個請求過來的時候都會產生一個新的 Action 對象實例,它並沒有和其他請求共享一個對象,所以不需要考慮線程安全問題。 

四、攔截器 

Interceptor(以下譯爲攔截器),在 AOP(Aspect-Oriented Programming)中用於在某個方法或字段被訪問之前,進行攔截然後在之前或之後加入某些操作。攔截是 AOP 的一種實現策略。 

在 Webwork 的中文文檔的解釋爲——攔截器是動態攔截 Action 調用的對象。它提供了一種機制可以使開發者定義在一個 action 執行的前後執行的代碼,也可以在一個 action 執行前阻止其執行。同時也提供了一種可以提取 action 中可重用的部分的方式。  

Struts1.x 的標準框架中不提供任何形式的攔截器,雖一個名爲 SAIF 的附加項目則實現了這樣的功能,但它的適用的範圍還很有限。 

攔截器是 Struts2的一個強有力的工具,有許多功能(feature)都是構建於它之上,如國際化、轉換器,校驗等。談到攔截器,還有一個流行的詞——攔截器 鏈(Interceptor Chain,在 Struts2中稱爲攔截器棧 Interceptor Stack)。攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,攔截器鏈中的攔截器就會按其之前定義的順序被調用。 

Struts 2的攔截器實現相對比較簡單。當請求到達 Struts2的 ServletDispatcher 時,Struts 2會查找配置文件,並根據其配置實例化相對的攔截器對象,然後串成一個列表(list),最後一一地調用列表中的攔截器。 

Struts 2已經提供豐富多樣功能齊全的攔截器實現。讀者可以到 struts2-all-2.0.6.jar 或 struts2-core-2.0.6.jar 包的 struts-default.xml 查看關於默認的攔截器與攔截器鏈的配置。 

作爲“框架(framework)”,可擴展性是不可缺少的,因爲世上沒有放之四海而皆準的東西。雖然,Struts 2爲我們提供如此豐富的攔截器實現,但是這並不意味我們失去創建自定義攔截器的能力,恰恰相反,在 Struts 2自定義攔截器是相當容易的一件事。 

五、Struts2和 Struts1.x的全面比較 

爲了對 Struts2和 Strtus1.x進行全面的比較,讓讀者瞭解這兩種框架各自的優缺點,以便於在自己的項目中,根據實際情況,選擇合適的框架,對它們兩者進行比較,總結了如下表分析比較。 

特性比較: 

Action 類

Struts1.x 要求 Action 類要擴展自一個抽象基類。Struts1.x 的一個共有的問題是面向抽象類編程而不是面向接口編程。

Struts2的 Action 類實現了一個 Action 接口,連同其他接口一起來實現可選擇和自定義的服務。Struts2提供一個名叫 ActionSupport 的基類來實現一般使用的接口。當然,Action接口不是必須的。任何使用 execute 方法的 POJO 對象可以被當作 Struts 2的 Action 對象來使用。 

線程模型

Struts1.x Action 類是單例類,因爲只有一個實例來控制所有的請求。單例類策略造成了一定的限制,並且給開發帶來了額外的煩惱。Action 資源必須是線程安全或者同步的。

Struts2 Action 對象爲每一個請求都實例化對象,所以沒有線程安全的問題。(實踐中,servlet容器給每一個請求產生許多丟棄的對象,並且不會導致性能和垃圾回收問題)。

 

Servlet 依賴

Struts1.x 的 Action 類依賴於 servlet API,當 Action 被調用時,以 HttpServletRequest 和HttpServletResponse 作爲參數傳給 execute 方法。

Struts2的 Action 和容器無關。Servlet 上下文被表現爲簡單的 Maps,允許 Action 被獨立的測試。Struts2的 Action 可以訪問最初的請求(如果需要的話)。但是,儘可能避免或排除其他元素直接訪問 HttpServletRequest 或 HttpServletResponse。 

易測性

測試 Struts1.x的主要問題是 execute 方法暴露了 Servlet API 這使得測試要依賴於容器)。第三方的擴展,如 Struts TestCase,提供了一套 Struts1的模擬對象(來進行測試)。

Struts2的 Action 可以通過初始化、設置屬性、調用方法來測試。依賴注入的支持也是測試變得更簡單。 

捕獲輸入

Struts1.x 使用 ActionForm 對象來捕獲輸入。象 Action 一樣,所有的 ActionForm 必須擴展基類。因爲其他的 JavaBean 不能作爲 ActionForm 使用,開發者經常創建多餘的類來捕獲輸入。DynaBeans 可以被用來作爲替代 ActionForm 的類來創建。但是,開發者可能是在重新描述(創建)已經存在的 JavaBean(仍然會導致有冗餘的 javabean)。

Struts2直接使用 Action 屬性作爲輸入屬性,消除了對第二個輸入對象的需求。輸入屬性可能是有自己(子)屬性的rich對象類型。Action 屬性能夠通過web頁面上的taglibs訪問。Struts2也支持 ActionForm 模式。rich對象類型,包括業務對象,能夠用作輸入/輸出對象。這種 ModelDriven 特性簡化了 taglib 對 POJO 輸入對象的引用。 

表達式語言

Struts1.x 整合 JSTL,所以它使用 JSTL 的表達式語言。表達式語言有基本的圖形對象移動,但是對集合和索引屬性的支持很弱。

Struts2使用 JSTL,但是也支持一個更強大和靈活的表達式語言--"Object Graph Notation Language" (OGNL)。 

將值綁定到頁面

Struts1.x 使用標準 JSP 機制來綁定對象到頁面上下文。

Struts2使用“ValueStack”技術,使 taglib 能夠訪問值而不需要把你的頁面(view)和對象綁定起來。ValueStack 策略允許通過一系列名稱相同但類型不同的屬性重用頁面(view)。 

類型轉換

Struts1.x 的 ActionForm 屬性經常都是 String。Struts 1.x 使用 Commons-Beanutils 來進行類型轉換。轉換每一個類,而不是爲每一個實例配置。

Struts2使用 OGNL 進行類型轉換。提供基本和常用對象的轉換器。 

驗證

Struts1.x 支持在 ActionForm 的 validate 方法中手動校驗,或者通過 Commons Validator 的擴展來校驗。同一個類可以有不同的校驗內容,但不能校驗子對象。

Struts2支持通過 validate 方法和 XWork 校驗框架來進行校驗。XWork 校驗框架使用爲屬性類類型定義的校驗和內容校驗,來支持 chain 校驗子屬性 

Action 執行控制

Struts1.x 支持每一個模塊有單獨的 Request Processors(生命週期),但是模塊中的所有 Action 必須共享相同的生命週期。

Struts2支持通過攔截器堆棧(Interceptor Stacks)爲每一個 Action 創建不同的生命週期。堆棧能夠根據需要和不同的 Action 一起使用。 

六、結論

前面已經簡要介紹了 Struts2的起源,並詳細對比了 Struts2和 Struts1.x 的差異,讀者應該對 Struts2的基礎有所瞭解了——包括高層的框架概念和基礎的請求流程,並理解 Struts1.x 和 Struts2兩者之間在 Action 方面的差別,Struts2加強了對攔截器與 IoC 的支持,而在 Struts1.x 中,這些特性是很難想象的。  

同時,讀者應該明白:Struts2是 WebWork 的升級,而不是 Struts 1.x 的升級。雖然 Struts 2提供了與 Struts1.x 的兼容,但已經不是 Struts1.x 的升級。對於已有 Struts1.x 開發經驗的開發者而言,Struts1.x 的開發經驗對於 Struts2並沒有太大的幫助;相反,對於已經有 WebWork 開發經驗的開發者而言,WebWork 的開發經驗對 Struts2的開發將有很好的借鑑意義。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章