王和全(ok_winnerboy@sina.com)
2003年 8 月
作為基于MVC模式的Web應用最經典框架,Struts已經正式推出了1.1版本,該版本在以往版本的基礎上,提供了許多激動人心的新功能。本文就將帶你走進Struts 1.1去深入地了解這些功能。
說明:希望本文的讀者能有一定的Struts使用基礎。
1、Model 2
Struts是基于Model 2之上的,而Model 2是經典的MVC(模型-視圖-控制器)模型的Web應用變體,這個改變主要是由于網絡應用的特性--HTTP協議的無狀態性引起的。Model 2的目的和MVC一樣,也是利用控制器來分離模型和視圖,達到一種層間松散耦合的效果,提高系統靈活性、復用性和可維護性。在多數情況下,你可以將Model 2與MVC等同起來。
下圖表示一個基于java技術的典型網絡應用,從中可以看出Model 2中的各個部分是如何對應于Java中各種現有技術的。
在利用Model 2之前,我們是把所有的表示邏輯和業務邏輯都集中在一起(比如大雜燴似的jsp),有時也稱這種應用模式為Model 1,Model 1的主要缺點就是緊耦合,復用性差以及維護成本高。
2、Struts 1.1 和Model 2
既然Struts 1.1是基于Model 2之上,那它的底層機制也就是MVC,下面是Struts 1.1中的MVC實現示意圖:
圖解說明:其中不同顏色代表MVC的不同部分:紅色(控制器)、紫色(模型)和綠色(視圖)
首先,控制器(ActionServlet)進行初始化工作,讀取配置文件(struts-config.xml),為不同的Struts模塊初始化相應的ModuleConfig對象。比如配置文件中的Action映射定義都保存在ActionConfig集合中。相應地有ControlConfig集合、FormBeanConfig集合、ForwardConfig集合和MessageResourcesConfig集合等。
提示:模塊是在Struts 1.1中新提出的概念,在稍后的內容中我們將具體介紹,你現在可以簡單地把模塊看作是一個子系統,它們共同組成整個應用,同時又各自獨立。Struts 1.1中所有的處理都是在特定模塊環境中進行的。模塊的提出主要是為了解決Struts 1.0中單配置文件的問題。
控制器接收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的關系也是如此。
下圖給出了客戶端從發出請求到獲得響應整個過程的圖解說明。
下面我們就來具體地討論一下其中的每個部分,在這之前,先來了解一下模塊的概念。
3、模塊
我們知道,在Struts 1.0中,我們只能在web.xml中為ActionServlet指定一個配置文件,這對于我們這些網上的教學例子來說當然沒什么問題,但是在實際的應用開發過程中,可能會有些麻煩。因為許多開發人員都可能同時需要修改配置文件,但是配置文件只能同時被一個人修改,這樣肯定會造成一定程度上的資源爭奪,勢必會影響開發效率和引起開發人員的抱怨。
在Struts 1.1中,為了解決這個并行開發的問題,提出了兩種解決方案:
支持多個配置文件,是指你能夠為ActionServlet同時指定多個xml配置文件,文件之間以逗號分隔,比如Struts提供的MailReader演示例子中就采用該種方法。
<!-- Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml, /WEB-INF/struts-config-registration.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
這種方法可以很好地解決修改沖突的問題,不同的開發人員可以在不同的配置文件中設置自己的Action、ActionForm等等(當然不是說每個開發人員都需要自己的配置文件,可以按照系統的功能模塊進行劃分)。但是,這里還是存在一個潛在的問題,就是可能不同的配置文件之間會產生沖突,因為在ActionServlet初始化的時候這幾個文件最終還是需要合并到一起的。比如,在struts-config.xml中配置了一個名為sUCcess的<forward>,而在struts-config-registration.xml中也配置了一個同樣的<forward>,那么執行起來就會產生沖突。
為了徹底解決這種沖突,Struts 1.1中引進了模塊(Module)的概念。一個模塊就是一個獨立的子系統,你可以在其中進行任意所需的配置,同時又不必擔心和其它的配置文件產生沖突。因為前面我們講過,ActionServlet是將不同的模塊信息保存在不同的ModuleConfig對象中的。要使用模塊的功能,需要進行以下的預備工作:
1、為每個模塊預備一個配置文件
2、配置web.xml文件,通知控制器
決定采用多個模塊以后,你需要將這些信息告訴控制器,這需要在web.xml文件進行配置。下面是一個典型的多模塊配置:
<init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value></init-param><init-param> <param-name>config/customer</param-name> <param-value>/WEB-INF/struts-config-customer.xml</param-value></init-param><init-param> <param-name>config/order</param-name> <param-value>/WEB-INF/struts-config-order.xml</param-value></init-param>
要配置多個模塊,你需要在原有的一個<init-param>(在Struts 1.1中將其對應的模塊稱為缺省模塊)的基礎之上,增加模塊對應的<init-param>。其中<param-name>表示為config/XXX的形式,其中XXX為對應的模塊名,<param-value>中還是指定模塊對應的配置文件。上面這個例子說明該應用有三個模塊,分別是缺省模塊、customer和order,它們分別對應不同的配置文件。
3、預備各個模塊所需的ActionForm、Action和JSP等資源
但是要注重的是,模塊的出現也同時帶來了一個問題,即如何在不同模塊間進行轉發?有兩種方法可以實現模塊間的轉發,一種就是在<forward>(全局或者本地)中定義,另外一種就是利用org.apache.struts.actions.SwitchAction。
下面就是一個全局的例子:
... <struts-config> ... <global-forwards> <forward name="toModuleB" contextRelative="true" path="/moduleB/index.do" redirect="true"/> ... </global-forwards> ... </struts-config>
可以看出,只需要在原有的path屬性前加上模塊名,同時將contextRelative屬性置為true即可。此外,你也可以在<action>中定義一個類似的本地<forward>。
<action-mappings> <!-- Action mapping for PRofile form --> <action path="/login" type="com.ncu.test.LoginAction" name="loginForm" scope="request" input="tile.userLogin" validate="true"> <forward name="success" contextRelative="true" path="/moduleA/login.do"/> </action> </action-mappings>
假如你已經處在其他模塊,需要轉回到缺省模塊,那應該類似下面這樣定義,即模塊名為空。
<forward name="success" contextRelative="true" path="/login.do"/>
此外,你也可以使用org.apache.struts.actions.SwitchAction,例如:
... <action-mappings> <action path="/toModule" type="org.apache.struts.actions.SwitchAction"/> ... </action-mappings> ...
4、ActionServlet
我們首先來了解MVC中的控制器。在Struts 1.1中缺省采用ActionServlet類來充當控制器。當然假如ActionServlet不能滿足你的需求,你也可以通過繼續它來實現自己的類。這可以在/WEB-INF/web.xml中來具體指定。
要把握ActionServlet,就必須了解它所扮演的角色。首先,ActionServlet表示MVC結構中的控制器部分,它需要完成控制器所需的前端控制及轉發請求等職責。其次,ActionServlet被實現為一個專門處理HTTP請求的Servlet,它同時具有servlet的特點。在Struts 1.1中它主要完成以下功能:
新聞熱點
疑難解答