Struts 的底層機制是MVC,下面是Struts 1.1中的MVC實現示意圖:

首先,控制器(ActionServlet)進行初始化工作,讀取配置文件(struts-config.xml),為不同的Struts模塊初始化相應的ModuleConfig對象。比如配置文件中的Action映射定義都保存在ActionConfig集合中。相應地有ControlConfig集合、FormBeanConfig集合、ForwardConfig集合和MessageResourcesConfig集合等。
控制器接收HTTP請求,并從ActionConfig中找出對應于該請求的Action子類,如果沒有對應的Action,控制器直接將請求轉發給JSP或者靜態頁面。否則控制器將請求分發至具體Action類進行處理。
在控制器調用具體Action的execute方法之前,ActionForm對象將利用HTTP請求中的參數來填充自己(可選步驟,需要在配置文件中指定)。具體的ActionForm對象應該是ActionForm的子類對象,它其實就是一個JavaBean。此外,還可以在ActionForm類中調用validate方法來檢查請求參數的合法性,并且可以返回一個包含所有錯誤信息的ActionErrors對象。如果執行成功,ActionForm自動將這些參數信息以JavaBean(一般稱之為form bean)的方式保存在Servlet Context中,這樣它們就可以被其它Action對象或者JSP調用。
Struts將這些ActionForm的配置信息都放在FormBeanConfig集合中,通過它們Struts能夠知道針對某個客戶請求是否需要創建相應的ActionForm實例。
Action很簡單,一般只包含一個execute方法,它負責執行相應的業務邏輯,如果需要,它也進行相應的數據檢查。執行完成之后,返回一個ActionForward對象,控制器通過該ActionForward對象來進行轉發工作。我們主張將獲取數據和執行業務邏輯的功能放到具體的JavaBean當中,而Action只負責完成與控制有關的功能。遵循該原則,所以在上圖中我將Action對象歸為控制器部分。
提示:其實在Struts 1.1中,ActionMapping的作用完全可以由ActionConfig來替代,只不過由于它是公共API的一部分以及兼容性的問題得以保留。ActionMapping通過繼承ActionConfig來獲得與其一致的功能,你可以等同地看待它們。同理,其它例如ActionForward與ForwardConfig的關系也是如此。
下圖給出了客戶端從發出請求到獲得響應整個過程的圖解說明。

ActionServlet
首先來了解MVC中的控制器。在Struts 1.1中缺省采用ActionServlet類來充當控制器,當然如果ActionServlet不能滿足需求,也可以通過繼承它來實現自己的類。可以在/WEB-INF/web.xml中來具體指定。
要掌握ActionServlet,就必須了解它所扮演的角色。首先,ActionServlet表示MVC結構中的控制器部分,它需要完成控制器所需的前端控制及轉發請求等職責。其次,ActionServlet被實現為一個專門處理HTTP請求的Servlet,它同時具有servlet的特點。在Struts 1.1中它主要完成以下功能:
接收客戶端請求;
根據客戶端的URI將請求映射到一個相應的Action類;
從請求中獲取數據填充Form Bean(如果需要);
調用Action類的execute()方法獲取數據或者執行業務邏輯;
選擇正確的視圖響應客戶。
此外,ActionServlet還負責初始化和清除應用配置信息的任務。ActionServlet的初始化工作在init方法中完成,它可以分為兩個部分:初始化ActionServlet自身的一些信息以及每個模塊的配置信息。前者主要通過initInternal、initOther和initServlet三個方法來完成。根據客戶端的URI將請求映射到一個相應的Action類;
從請求中獲取數據填充Form Bean(如果需要);
調用Action類的execute()方法獲取數據或者執行業務邏輯;
選擇正確的視圖響應客戶。
我們可以在/WEB-INF/web.xml中指定具體的控制器以及初始參數。
ActionForm
對于ActionForm你可以從以下幾個方面來理解:
1、ActionForm表示HTTP窗體中的數據,可以將其看作是模型和視圖的中介,它負責保存視圖中的數據供模型或者視圖使用。Struts 1.1文檔中把它比作HTTP和Action之間的防火墻,這體現了ActionForm具有的過濾保護的作用,只有通過ActionForm驗證的數據才能夠發送到Action處理。
2、ActionForm是與一個或多個ActionConfig關聯的JavaBean,在相應的action的execute方法被調用之前,ActionForm會自動利用請求參數來填充自己(初始化屬性)。
3、ActionForm是一個抽象類,必須通過繼承來實現自己的類。
4、ActionForm首先利用屬性的getter和setter方法來實現初始化,初始化完畢后,ActionForm的validate方法被調用,可以檢查請求參數的正確性和有效性,并且可以將錯誤信息以ActionErrors的形式返回到輸入窗體。否則,ActionForm將被作為參數傳給action的execute方法以供使用。
5、ActionForm bean的生命周期可以設置為session(缺省)和request,當設置為session時,記得在reset方法中將所有的屬性重新設置為初始值。
6、由于ActionForm對應于HTTP窗體,所以隨著頁面的增多,ActionForm將會急速增加。而且可能同一類型頁面字段將會在不同的ActionForm中出現,并且在每個ActionForm中都存在相同的驗證代碼。為了解決這個問題,可以為整個應用實現一個ActionForm或者至少一個模塊對應于一個ActionForm。
但是,聚合的代價就是復用性很差,而且難維護。針對這個問題,在Struts 1.1中提出了DynaActionForm的概念。
DynaActionForm類
DynaActionForm的目的就是減少ActionForm的數目,利用DynaActionForm不必創建一個個具體的ActionForm類,而是在配置文件中配置出所需的虛擬ActionForm。例如,在下表中通過指定<form-bean>的type為"org.apache.struts.action.DynaActionForm"來創建一個動態的ActionForm--loginForm。
<form-beans>
<form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="actionClass" type="java.lang.String"/>
<form-property name="username" type="java.lang.String"/>
<form-property name="password" type="java.lang.String"/>
</form-bean>
</form-beans>
動態的ActionForm的使用方法跟普通的ActionForm相同,但是要注意一點:普通的ActionForm對象需要為每個屬性提供getter和setter方法,如果使用DynaActionForm,它將屬性保存在一個HashMap類對象中,同時提供相應的get(name) 和 set(name)方法,其中參數name是要訪問的屬性名。例如要訪問DynaActionForm中username的值,可以采用類似的代碼:
String username = (String)form.get("username");
由于值存放于一個HashMap對象,所以要記得對get()方法返回的Object對象做強制性類型轉換。正是由于這點區別,如果在Action中非常頻繁地使用ActionForm對象,建議還是使用普通的ActionForm對象。
Action
我們通過繼承Action類來實現具體的執行類。具體Action類的功能一般都在execute(以前是perform方法)方法中完成,其中主要涉及到以下幾個方面:
a、輔助ActionForm進行一些表單數據的檢查;
b、執行必要的業務邏輯,比如存取數據庫,調用實體bean等;
c、更新服務器端的bean數據,后續對象中可能會用到這些數據,比如在JSP中利用bean:write來獲得這些數據;
d、根據處理結果決定程序的去處,并以ActionForward對象的形式返回給ActionServlet。
b、執行必要的業務邏輯,比如存取數據庫,調用實體bean等;
c、更新服務器端的bean數據,后續對象中可能會用到這些數據,比如在JSP中利用bean:write來獲得這些數據;
d、根據處理結果決定程序的去處,并以ActionForward對象的形式返回給ActionServlet。
提示:由于在Action和ActionForm中都可以實現驗證方法,那么如何來安排它們之間的分工呢?一般來說,我們秉著MVC分離的原則,也就是視圖級的驗證工作放在ActionForm來完成,比如輸入不能為空,email格式是否正確,利用ValidatorForm可以很輕松地完成這些工作。而與具體業務相關的驗證則放入Action中,這樣就可以獲得最大ActionForm重用性的可能。
前面提到過,我們主張將業務邏輯執行分離到單獨的JavaBean中,而Action只負責錯誤處理和流程控制。而且考慮到重用性的原因,在執行業務邏輯的JavaBean中不要引用任何與Web應用相關的對象,比如HttpServletRequest,HttpServletResponse等對象,而應該將其轉化為普通的Java對象。