大多數人都會熟悉Struts, 無論是從項目實戰中獲得的經驗還是從書中了解到的知識。在這一系列文章裡,我們將通過一個由Struts遷移到Struts 2的簡單應用例子向大家展現Struts 2的所有特征。
在我們開始介紹這個例子之前,你需要去了解一點Struts 2的背景知識。文章的第一部分將介紹Struts 2與Struts的核心架構的不同點,以助於更好地把所有概念聯系起來。第二部分將深入探討兩者在actions上的差別、action相關的框架特征和action的配置。在文章最後一部分將會講述用戶界面。我們會講到其架構、UI構件、主題和標簽,還有如何為我們的應用加上新的外觀。
我們並不打算談及遷移過程的所有細節方面,我們只是從普通的出發點開始介紹Struts 2的概念和現在可用的所有新特征。但擁有這些知識後,無論以後遷移到何等規模的應用到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,改進功能和繼續添加新功能。
請求運作過程
在我們開始詳細探討如何把應用由Struts遷移到Struts 2之前,讓我們通過體驗整個請求流程,看看新架構是如何運作的。
在我們體驗了整個請求的生命周期後,你應當注意到很重要的一點——Struts 2仍是以前端控制器框架為主體的。所有的概念還都是你以前所熟悉的。
這意味著:
Actions仍然是通過URL觸發的
數據仍然是通過URL請求參數和Form參數傳送到服務端的
所有Servlet對象(如request、response和session等)仍在Action可用
以下是請求處理過程的高層概覽:
整個請求的處理過程可以分為6步:
由框架產生一個請求並進行處理 - 框架根據請求匹配相應的配置,得到使用哪些攔截器,Action類和返回結果的信息。
請求通過一系列的攔截器 - 攔截器和攔截器組可以按照不同級別進行組合配置來處理請求。它們為請求提供各種預處理和切面處理的應用功能。這和Struts的使用Jakarta Commons Chain構件的RequestProcessor類很相似。
調用Action - 產生一個新的Action對象實例,並提供請求所調用的處理邏輯的方法。我們在第二部文章中將對這步驟進行進一步討論。Struts 2可以在配置Action時為請求分配其指定的方法。
調用相應的Result - 通過匹配處理Action方法之後的返回值,獲取相應Result類,生成並調用它的實例。處理Result可能產生的結果之一就是對UI模板(但並非只有一個)進行渲染,來產生HTML。如果是這種情況的話,模板中的Struts 2 tags可以直接從Action中獲取要被要被渲染的值。
請求再次經過一系列攔截器處理後返回 - Request以和進入時相反的方向通過攔截器組,當然,你可以在這個過程中進行回收整理或者額外的處理工作。
響應被返回給用戶 - 最後一步是將控制權交還給Servlet引擎。最常見的結果是把渲染後的HTML返回給用戶,但返回的也可能是指定的HTTP頭或者進行HTTP重定向。
你應該已經注意到Struts 2和Struts的差別了。最明顯的就是Struts 2是一個pull-MVC架構。這是什麼意思呢?從開發者角度看,就是說需要顯示給用戶的數據可以直接從Action中獲取,而不像Struts那樣必須把相應的Bean存到Page、Request或者Session中才能獲取。
配置框架
首先最重要的是,通過配置web.xml文件讓框架能在Servlet容器裡運行。
下面這個就是大家都熟悉的Struts在web.xml裡的配置方法:
action
org.apache.struts.action.ActionServlet
config
/WEB-INF/struts-config.xml
2
action
*.do
在Struts 2中,配置有少許改變,最明顯的是分發器(dispatcher)已由Servlet轉為Servlet Filter, 其配置和Servlet一樣簡單,如下:
webwork
org.apache.struts.action2.dispatcher.FilterDispatcher
webwork
/*
和Servlet配置一樣,Filter配置中定義了Filter的名稱(作為引用)和類名。Filter Mapping通過URI和名稱匹配來調用相應的Filter。默認情況下,擴展名為“.action”,這是在default.properties文件(在Struts 2 JAR文件裡)的“struts.action.extension”屬性定義的。
工具箱:“default.properties”是默認配置選項定義文件。你可以通過在classpath中包含一個叫“struts.properties”的文件,設置不同的屬性值,來覆蓋默認配置的值,實現自己的配置。
對於Struts來說, Servlet配置提供了一個用於定義文件名的init-param tag來配置Struts,而Struts 2沒有這樣的配置參數,取而代之的是在classpath下的默認配置文件“struts.xml”。
工具箱/提示:因為Struts Actions(擴展名“.do”)和Struts 2 Actions(擴展名“.action”)兩者的擴展名命名空間不一樣,所以Struts和Struts 2可以在同一個Web應用系統中無礙地共存。所以這就為遷移提供了很好的條件,加入適當的配置,新功能的開發都用Struts 2。保持原有的遺留功能,如果時間和資源允許的情況下再逐步遷移。另一種方法是,只把Struts 2的擴展名改為“.do”,這樣就可使得以前的JSP頁面可重用。
解剖Actions
在上面介紹的請求運作流程中,我們從高層次上談及了一些Struts和Struts 2的不同點。現在我們將較深入地探討這兩個框架中Action結構的具體差別。
讓我們來回顧一下Struts的Action的主要結構。Struts Action的主要形式如下:
public class MyAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// do the work
return (mapping.findForward("success"));
}
}
當你實現一個Struts Action時, 需要注意以下問題:
所有的Action都繼承於Action基類。
所有的Action都必須是線程安全的,因為只產生一個Action實例。
因為所有的Action都必須是線程安全的,所有在Action處理過程中所需要的對象都必須以方法參數的形式傳入。
處理Action所調用的方法必須命名為“execute”(在Struts中的DispatchAction類可以調用同一個Action的其它方法,但實際上在框架中的入口點仍然是“execute”方法)。
ActionForward結果是通過ActionMapping類中的方法來產生的,通常的做法是通過調用“findForward”方法。
相比較之下,Struts 2的Action提供了更簡單的實現方式。下面就是個例子:
public class MyAction {
public String execute() throws Exception {
// do the work
return "success";
}
}
首先你會注意到的是,Struts 2中的Action不再繼承於任何類或需要實現任何接口。實際上,它還遠不只這些。按照慣例,只有“execute”方法能調用Action, 但在Struts 2中並非必要,任何聲明為public String methodName() 方法都能通過配置來調用Action。
另外,你會注意到返回的對象不是ActionForward,而是String。如果你不喜歡以字符串的形式出現在你的代碼中,有個Helper接口Action可以以常量方式提供常見結果,如“success”、“none”、“error”、“input”和“login”。
最後,和Struts最大的革命性的不同是,處理Action過程中調用的方法(“execute”方法)是不帶參數的。那你如何獲取你所需要的對象呢?答案是使用“反轉控制(Inversion of Control)”,也叫“依賴注入(Dependency Injection)”的模式(想更多地了解這方面信息請看Martin Fowler的文章http://www.martinfowler.com/articles/injection.html)。Spring框架使得這個模式流行起來,然而Struts 2的前身(WebWork)也在同時應用上了這個模式。
為了更好地了解反轉控制,讓我們來看看一個例子,如何在Action處理過程中可以訪問到當前請求HttpServerRequest對象。
在我們的例子中,我們使用的依賴注入機制是接口注入。就如其名稱一樣,接口注入需要的是已經被實現了的接口。這個接口包含了相應屬性的setter,為Action提供值。例子中我們使用了ServletRequestAware接口,如下:
public interface ServletRequestAware {
public void setServletRequest(HttpServletRequest request);
}
當我們繼承這個接口後,我們原本簡單的Action看起來有點復雜了,但是這時我們可以獲取HttpServerRequest對象來使用了。
public class MyAction implements ServletRequestAware {
private HttpServletRequest request;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public String execute() throws Exception {
// do the work using the request
return Action.SUCCESS;
}
}
看起來現在這些屬性是類級別的,並不是線程安全的,會出現問題。其實在Struts 2裡並沒有問題,因為每個請求過來的時候都會產生一個新的Action對象實例,它並沒有和其他請求共享一個對象,所以不需要考慮線程安全問題。
現在我們還有最後一步,就是為這個Action關聯上ServletConfigInterceptor攔截器。這個攔截器提供了一系列功能去獲取HttpServletRequest,並可以把它注入到實現了ServletRequestAware接口Action中。這時你並不需要擔心如何配置這些,我們將在下一篇文章中有具體講述。最重要的是讓我們明白到是攔截器和接口共同合作下為Action提供了反轉控制功能的。
這個設計的好處是能讓Action和框架完全解耦。Action僅僅是一個與框架無關的簡單POJO。這給單元測試帶來極大的好處,Struts 2 Action的單元測試遠比Struts Action使用StrutsTestCase或MockStrutsTestCase的單元測試簡單。
總結/綜述
到現在為止,你應該對Struts2的基礎有所了解了——包括高層的框架概念和基礎的請求流程。你也應該能自己動手在Servlet容器裡配置Struts 2,並理解Struts和Struts 2兩者之間在Action方面的差別了。
在下篇文章中,我們將會介紹一個詳細的遷移的例子,同時我們也在這章學到的知識基礎上,演示如何由Struts向Struts 2的Action遷移。在最後講述如何在應用中使用JSTL、JSP和Struts2。我們會進一步探討Struts和Struts 2在Action上的區別,Struts 2的配置和其他框架元素,和談到更多的Action相關的框架特征。