- Java EE框架開發技術與案例教程
- 張繼軍 董衛
- 6175字
- 2020-05-28 14:20:32
3.5 Struts 2的控制層及Action設計與配置
開發基于Struts 2的Web應用程序時,Action是程序的核心。開發人員需要根據業務邏輯實現特定的Action類,并在struts.xml文件中配置Action。Action類中包含了對用戶請求的處理邏輯,因此,也把Action稱為Action業務控制器。
一般來說,每個Action類都有一個或多個方法來實現用戶請求的處理,其中的每個方法會返回一個String類型的處理結果,該String值用于決定需要跳轉(轉向或重定向)到哪個視圖或者另一個Action。
對于一個Action,需要傳入值和傳出值,即由請求傳值給Action, Action處理加工后傳值給視圖(或另一個Action)。
本節主要介紹Action類的設計方法及Action的配置方法,以及Action傳值方法,另外,還將介紹Action訪問Web容器中的Web對象(request、response、session)的方法(Servlet API)。
3.5.1 Action類的實現與傳值
Struts 2對Action的實現給出了不同的方法,實現方法不同,其接受參數(由提交信息頁面提交給Action容器)的方式也不同,本小節分4種方式來討論Action類的創建及接受參數的問題。
● 普通的Java類作為Action類與屬性驅動傳參。
● 繼承ActionSupport實現Action與屬性驅動傳參。
● 領域對象屬性驅動的Action類設計與屬性驅動傳參。
● 模型驅動(Model-Driven)的Action類與模型驅動傳參。
無論采用上面4種方法的哪一種方式,設計Action類都必須滿足以下條件:
1)Action類,每個屬性都有sette/getterr方法,必須具有無參的構造方法。
2)該類除所必需的屬性外,通常有一個execute()方法(或自定義方法),該方法無任何參數且返回字符串類型的值。
3)JSP頁面與Action參數傳遞:JSP頁面中使用的輸入域屬性(name屬性值、EL表達式中的屬性名)必須與Action類定義中的屬性名稱相同。
1.普通的Java類作為Action類與屬性驅動傳參
【例3-3】采用“普通的Java類作為Action類與屬性驅動傳參”方式,實現Action的設計與配置,實現【例3-1】中所要求的功能。
【分析】采用一般的Java類設計該Action類(SumAction.java),其中,屬性x、y分別為加數和被加數,而sum用于存放計算結果;其方法為CalculateSum()計算和值,當和值為非負數時,返回“+”,否則返回“-”。
【實現】重新設計Action,其代碼如下,注意其中的注釋并與【例3-1】中代碼對比。
package com.action; public class SumAction { private double x; //加數,頁面接受該參數值 private double y; //被加數,頁面接受該參數值 private double sum; //和值,將和值傳出給其他視圖或Action public String CalculateSum(){ //完成計算功能并返回字符串(頁面邏輯值)的方法 String forward="-"; sum=x+y; //求和并存儲于屬性sum中,可以在其他視圖或Action中訪問sum if(sum>=0){ forward="+"; } return forward; } public SumAction() {}//無參構造方法 public SumAction(double x, double y) {//帶參構造方法,但只有兩個參數x、y this.x = x; this.y = y; this.sum =x+y; } //省略了各屬性的setter/getter方法 }
【修改【例3-1】中的配置及其頁面】
由于Action的實現修改了,其相應的配置文件及提交信息的頁面和展示信息的頁面都要進行相應的修改,其修改后的新內容如下。
(1)修改struts.xml配置文件,配置Action

(2)修改JSP頁面
修改提交數據頁面:input.jsp,代碼如下。

修改positive.jsp和negative.jsp代碼中的代碼(修改其中的EL表達式如下)。
代數和為非負整數,其和值為:${x}+${y}=${sum}>
當運行程序時,其運行結果與【例3-1】完全一樣。
2.繼承ActionSupport實現Action與屬性驅動傳參
為了能夠開發出更加規范的Action類,Struts 2提供了Action接口,并且為Action接口提供一個實現類ActionSupport。
(1)Action接口
Action接口定義了Struts 2的Action類中應該使用的規范,給出有關的字符串常量和一個方法。Struts 2類庫中的Action接口(Action.java)的代碼如下。
public interface Action { public static final String SUCCESS = "success"; public static final String NONE = "none"; public static final String ERROR = "error"; public static final String INPUT = "input"; public static final String LOGIN = "login"; public String execute()throws Exception; //聲明方法 }
(2)Action接口的實現類ActionSupport
Struts 2為Action接口提供了一個實現類ActionSupport,其中的3個主要方法如下。
● void addFieldError(String fieldname, String errorMessage):添加字段驗證的出錯信息。
● String exeute():請求時執行的方法,需要重載。
● void validate():用于輸入驗證的方法。
在編寫業務Action類時,建議繼承ActionSupport實現Action類,并重寫exeute()方法,或者添加新方法。對于addFieldError()和validate(),將在第4章給出詳細介紹。
【例3-4】采用繼承類ActionSupport的方式,重新設計Action。完成【例3-1】所要求的功能。假設要設計的Action為SumActionSupport.java,其代碼如下。

修改struts.xml配置文件,配置Action。

方法execute()是Action默認方法,可以省略方法配置屬性,簡化為如下代碼。
<action name="opadd"class="com.action.SumActionSupport">
其他組件都不需要修改。
3.領域對象的Action類設計與屬性驅動傳參
在實際應用中,根據需要,將各類信息設計成有關的實體類,在【例3-1】和【例3-2】中,都是首先設計了模型類,然后再設計Action,且Action中的一個屬性是某實體類的對象屬性。例如,【例3-1】中設計的實體類Add.java和Action類AddAction.java。
將這種設計Action的方法稱為“領域對象屬性驅動的Action”設計方法。在實際應用系統中采用較多的是這種方法。對于【例3-2】用戶的登錄與注冊,也是采用這種方法設計的。
4.模型驅動(Model-Driven)的Action類與模型驅動傳參
模型驅動是Struts 2獨有的一種接收用戶輸入的機制。采用“模型驅動”設計Action類,必須實現ModelDriver接口,必須實現getMode()方法,該方法把Action及對應的Model實例(Action中聲明的屬性,該屬性一定要實例化)關聯。
【例3-5】采用模型驅動重新設計Action,實現【例3-1】所要求的功能。
采用“模型驅動設計Action”,必須先設計模型類,在【例3-1】中,已經設計了模型類Add,在此基礎上設計Action類。這里也繼承ActionSupport。
1)設計Action:要設計的Action為SumActionModelDriven.java,其代碼如下。

2)修改struts.xml配置文件,配置Action。

3)修改JSP頁面。
修改提交數據頁面:input.jsp代碼。

修改positive.jsp和negative.jsp代碼中的代碼(都修改為如下)。

4)當運行程序時,其運行結果與【例3-1】完全一樣。
3.5.2 基于XML配置文件的Action配置與訪問
在3.4.3節中已經較詳細地介紹了struts.xml中常用的配置信息及配置方法,其中Action的配置最為重要,Action的配置方法不同,調用(訪問)Action的方法也不同。在本小節中,進一步強化Action的配置,以及Action的調用方法。
1.Action配置
Struts 2中Action類的配置能夠讓Struts 2知道Action的存在,并可以通過調用該Action來處理用戶請求。Struts 2使用包來組織和管理Action。
在一個Struts 2配置文件中,可以配置多個包,而每個包內可以再配置多個Action。配置格式及配置方法在前面已經介紹過,具體內容見3.4.3節。
2.如何訪問Action
對所設計的Action,通過“包配置”和“Action配置”后,實際上就制定了訪問使用Action的方式。
訪問Struts 2中Action的URL由兩部分組成:“包的命名空間”+“Action的名稱”。
在圖3-9中,其訪問地址形成過程(注意圖3-10中兩種訪問Action的差異)如下。

圖3-9 訪問Action的URL形成策略

圖3-10 【例3-6】的提交頁面
a) 提交頁面b) 顯示運行結果頁面
1)對于配置的包tt1,其namespac="/test1",而Action的name="xyz",所以,形成的地址是:"/test1"與"xyz"的鏈接(注意,兩者之間自動添加“/”),即地址為:"/test/xyz.actin"。
2)同樣,對于配置包tt2,以及其該包下的Action配置,形成的訪問地址為:"/test2/xyz/abcd.action"。
重要提示:
1)由于基于Struts 2開發Web應用程序,其實有兩個容器(Action容器和Servlet容器),兩個容器管理資源的相對根目錄是不同的。
2)Action容器根目錄:服務器的IP+:“端口號”+工程部署后根目錄(一般為工程名稱)。
3)Servlet容器根目錄:服務器的IP+:“端口號”。
4)在Action容器內訪問資源的通用格式(包括在Action中配置的JSP頁面):
/按配置Action規范形成的訪問Action的地址
注意:這里的首符號“/”必須有,表示從Action容器根目錄開始尋找訪問資源,若不是從Action容器根目錄開始尋找,則最好給出要訪問資源的絕對路徑。
5)“超鏈接”操作是在“Servlet容器”內進行的,所以,在基于Struts 2設計的Web程序中,對于超鏈接,最好采用絕對路徑訪問資源。
3.5.3 多方法的Action設計與配置訪問
前面所定義的Action都是一個方法配置一個Action。在實際的應用中,可以用一個Action處理多個業務請求,并在struts.xml指定業務處理所采用的方法。
【例3-6】對于【例3-1】,只實現了兩個實數的求和運算。在本例中,要求修改該程序,完成兩個數的四則運算(加、減、乘、除),提交頁面如圖3-10a所示,單擊“求和”按鈕,將完成求和運算并顯示如圖3-10b的結果,對于其他3個提交按鈕類似。
【分析】在提交頁面中有4個提交按鈕,分別完成不同的計算功能,4種運算在一個Action中定義4種不同的方法來實現,這些方法的格式和execute()方法一樣。本案例采用“領域對象的Action類設計與屬性驅動傳參”方式實現。
【實現】
1)首先設計一個領域類:Calculate.java,其代碼如下。
package com.model; public class Calculate { private double x; // 第1個操作數,從提交頁面獲取信息的屬性 private double y; // 第2個操作數,從提交頁面獲取信息的屬性 //省略了各屬性的setter/getter方法 public double add(){ return x+y; }// 求和方法 public double sub(){return x-y; }// 求差方法 public double mul(){return x*y; }// 求積方法 public double div(){return x/y; }// 求商方法,這里假設y不會出現0的情況 }
2)設計Action:CalculateAction.java,其代碼如下。
package com.action; //省略了import; public class CalculateAction extends ActionSupport { private Calculate data; // 對象屬性 private double value; // 用于存放計算結果,結果傳給顯示頁面 private String msg; // 用于存放計算信息,結果傳給顯示頁面 //省略了各屬性的setter/getter方法 public String add()throws Exception{// 求和方法 value = data.add(); msg = "你選擇的是求和運算!"; return "show"; } public String sub()throws Exception{// 求差方法 value = data.sub(); msg = "你選擇的是求差運算!"; return "show"; } public String mul()throws Exception{// 求積方法 value = data.mul(); msg = "你選擇的是求積運算!"; return "show"; } public String div()throws Exception{// 求商方法 value = data.div(); msg = "你選擇的是求商運算!"; return "show"; } }
3)設計提交數據頁面:input.jsp,其代碼如下。
<head> <title>提交數據頁面,并根據不同的按鈕選擇不同的業務處理</title> <script type="text/javascript"> function sub(){ document.aaa.action="sub"; } //動態修改表單的Action屬性 function mul(){ document.aaa.action="mul"; } //動態修改表單的Action屬性 function div(){ document.aaa.action="div"; } //動態修改表單的Action屬性 </script> </head> <body> <form action="add" method="post" name="aaa"> 請輸入兩個整數:<br><br> 第1個運算數:<input name="data.x"/><br><br> 第2個運算數:<input name="data.y"/><br><br> <input type="submit" value="求和"/> <input type="submit" value="求差" onclick="sub()"/> <input type="submit" value="求積" onclick="mul()"/> <input type="submit" value="求商" onclick="div()"/> </form> </body>
4)設置顯示運行結果的頁面:show.jsp,其主要代碼如下。
<body> ${msg} <br>第1個數為:${data.x} <br>第2個數為:${data.y} <br>運算結果為:${value} </body>
對于【例3-6】,在一個Action中,定義4個(多個)方法,如何配置這種情況下的Action呢?這種Action的配置及調用有以下3種方式。
● 為Action配置method屬性。
● 動態方法調用。
● 使用通配符映射方式。
1.為Action配置method屬性
Struts 2中為Action配置method屬性值,需要在struts.xml中配置Action中的每個方法,而且每個Action配置中都要指定method屬性。
對于【例3-6】,在struts.xml中配置Action,配置內容如下。
<package name="default" namespace="/" extends="struts-default"> <action name="add" class="com.action.CalculateAction" method="add"> <result name="show">/show.jsp</result> </action> <action name="sub" class="com.action.CalculateAction" method="sub"> <result name="show">/show.jsp</result> </action> <action name="mul" class="com.action.CalculateAction" method="mul"> <result name="show">/show.jsp</result> </action> <action name="div" class="com.action.CalculateAction" method="div"> <result name="show">/show.jsp</result> </action> </package>
2.動態方法調用
動態方法調用Action,其調用格式如下。
所配置的Action訪問路徑!方法名稱
注意:在默認情況下,Struts 2的動態方法調用處于禁用狀態,若要使用動態方法調用,需要在配置文件內配置允許動態調用信息,注意下面標注的說明。
在struts.xml中只需配置該Action,而不必配置每個方法,配置格式如下。

對于【例3-6】,在struts.xml中配置Action,配置內容可修改如下。

對于【例3-6】,還需要修改提交數據頁面,修改的代碼如下。

3.使用通配符映射方式
在struts.xml文件中配置Action元素時,它的name屬性支持通配符。當使用通配符時,相當于用一個元素Action定義了多個邏輯Action。配置格式如下。

其中,用戶請求的URL的模式是“Action名稱_*”;同時,method屬性值是一個表達式{1},表示它的值為name屬性值中第一個“*”的值。
對于【例3-6】,在struts.xml中配置Action,配置內容如下。

同時,需要對【例3-6】修改提交數據頁面input.jsp,修改的代碼如下。

思考:對比3種配置方式,各自的優點和缺點是什么?
3.5.4 Action訪問Web資源
在Struts 2中,Action類和Web對象之間沒有直接關系,但是Action作為業務邏輯控制器,經常要訪問Web資源。例如,在Action中要獲取Web容器中的request和session對象中的信息。Struts 2提供了ActionContext類與ServletActionContext類用于Action訪問Web資源,并且ServletActionContext類直接繼承了ActionContext類。
在Action中訪問Web對象有4種方式。
● 通過ServletActionContext直接訪問Web對象——Servlet依賴容器方式。
● 通過ActionContext訪問——Map依賴容器方式。
● 通過IoC訪問Servlet對象——Map IoC方式。
● 通過IoC訪問Servlet對象——Servlet IoC方式。
下面通過對【例3-3】(基于3個屬性:x、y、sum設計的Action)進行修改,分別給出這4種訪問方式的實現方法。
【例3-7】修改【例3-3】,將提交的加數x保存到request中,被加數y保存到session中,而和值sum保存在application中,并分別從request、session和application中獲取數據并顯示出來。
【分析】對于該題目,需要在Action中訪問Web對象,并實現數據的保存,所以,只需要修改【例3-3】的Action,另外,需要修改顯示頁面(positive.jsp和negative.jsp)。
【實現】由于有4種方式通過Action訪問Web對象,因此可以有4種實現方式。
1.利用ServletActionContex訪問Web對象——Servlet依賴容器方式
Struts 2框架提供org.apache.struts2.ServletActionContext輔助類來獲得Web對象。
HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); ServletContext application = ServletActionContext.getServletContext();
而HttpSession session的獲取需要兩步。
HttpServletRequest request = ServletActionContext.getRequest(); HttpSession session = request.getSession();
【實現方式1】
1)對于【例3-3】,重新設計Action:SumAction.java,其代碼如下。

2)提交數據頁面input.jsp和struts.xml不需要修改。
3)修改顯示頁面positive.jsp,其代碼如下。
<body> 代數和為非負整數:${sum}<br> 加數:${request.x2}><br> 被加數:${session.y2}><br> 和值:&{application.sum2}><br> </body>
4)對于negative.jsp的修改,與positive.jsp類似,讀者自己給出。
2.通過ActionContext訪問——Map依賴容器方式(與Servlet API解耦訪問方式)
Struts 2對HttpServletRequest、HttpSession和ServletContext進行了封裝,構造了3個Map對象,在Action中直接使用HttpServletRequest、HttpServletSession和ServletContext對應的Map對象來保存和讀取數據。ActionContext類中的常用方法如下。
● public static ActionContext getContext():獲得ActionContext對象。
● public Object get(Object key):在ActionContext中查找key的值。
● public void put(Object key, Object value):向當前ActionContext存入值。
● public void setApplication(Map application):設置application的值。
● public void setSession(Map session):設置session的值。
● public Map getParameters():從請求對象中獲取請求參數。
● public Map getApplication():獲取ServletContext中保存的Attribute。
● public Map getSession():獲取HttpSession中保存的Attribute。
若要實現Web的訪問,首先需要獲取ActionContext對象,然后利用該對象獲取request、session和application共3個對象,這3個對象的類型是Map類型的元素。
【實現方式2】
1)對于【例3-3】,要重新設計SumAction.java,其代碼如下。

2)對于顯示頁面:positive.jsp和negative.jsp,與【實現方式1】中的頁面一樣。
3.通過IoC訪問Servlet對象——Map IoC方式
在Struts 2框架中,通過IoC方式將Servlet對象注入到Action中,需要在Action中實現以下接口中的一個或多個。
(1)org.apache.struts2.interceptor.RequestAware
該接口有void setRequest(Map map)方法,實現該接口可訪問HttpServletRequest對象。
(2)org.apache.struts2.interceptor.SessionAware
該接口有void setSession(Map map)方法,實現該接口可訪問HttpSession對象。
(3)org.apache.struts2.interceptor.ApplicationAware
該接口有void setApplication(Map map)方法,實現該接口可訪問ServletContext對象。
【實現方式3】
1)對于【例3-3】,重新設計Action:SumAction.java,其代碼如下。

2)對于顯示頁面:positive.jsp和negative.jsp,與【實現方式1】中的頁面一樣。
4.通過IoC訪問Servlet對象——Servlet IoC方式
在Struts 2框架中,通過IoC方式將Servlet對象注入到Action中,需要在Action中實現以下接口。
(1)org.apache.struts2.interceptor.ServletContextAware
該接口有void setServletContext(ServletContext servletContext)方法,實現該接口的Action可以直接訪問ServletContext對象。
(2)org.apache.struts2.interceptor.ServletRequestAware
該接口有void setServletRequest(HttpServletRequest ruquest)方法,實現該接口的Action可以直接訪問HttpServletRequest對象。
(3)org.apache.struts2.interceptor.ServletResponseAware
該接口有void setServleResponse(HttpServletResponse response)方法,實現該接口的Action可以直接訪問HttpServletResponse對象。
【實現方式4】
1)對于【例3-3】,重新設計Action:SumAction.java,其代碼如下。

2)對于顯示頁面:positive.jsp和negative.jsp,與【實現方式1】中的頁面一樣。
本小節給出了4種從Action獲取Servlet對象的方法,在實際應用中,可以根據項目特點選擇其中之一。
3.5.5 基于注解的Action配置
前面介紹的Action定義和應用都是采用XML配置文件實現Action的配置與調用的,當應用程序較復雜時,配置文件會很龐大、復雜。
Struts 2提供了注解開發jar包:struts2-convention-plugin-2.3.*.jar,實現了注釋配置。使用時,需要復制struts-2.2.1\lib\struts2-convention-plugin-2.3.*.jar到當前Web應用的Web-INF的lib目錄下。
Struts 2使用注解開發必須遵循的規范:第一,Action必須要繼承ActionSupport父類;第二,Action所在的包名必須以.action結尾。
基于注解的配置,分為在類級別上的注解和在方法級別上的注解兩種。
1.標注在Action類上方的注釋——實現包注解配置
其注釋格式如下。
@ParentPackage(value="要繼承的包名稱") //表示繼承的父包 @Namespace(value="/命名空間名稱") //表示當前Action所在的命名空間
其中各參數的含義如下。
● @ParentPackage:對應xml配置文件中的package的父包,一般需要繼承struts-default。
● @Namespace:對應配置文件中的nameSpace,命名空間。
2.標注在Action類中方法上面的注解——實現Action注解配置
其注釋格式如下。
@Action( //表示請求的Action及處理方法 value="login", //表示Action的請求名稱 results={ //表示結果跳轉 @Result(name="success", location="/success.jsp", type="redirect"), @Result(name="login", location="/login.jsp", type="redirect"), @Result(name="error", location="/error.jsp", type="redirect") }, )
其中,該注釋中的屬性有以下兩個。
● value="名稱":表示Action的請求名稱,也就是<action>結點中的name屬性。
● results={}:表示Action的多個result,是一個數組(集合)屬性。
3.對于Action的屬性results中@Result的注解
其注釋格式如下。
@Result(name=" ", location=" ", type=" "),
其中各屬性的含義如下。
● name="名稱",表示Action方法返回值,即<result>結點的name屬性,默認為success。
● location="路徑名",表示要跳轉的新響應位置,既可以是相對路徑,也可以是絕對路徑。
● type="跳轉類型",框架默認的是dispatcher。
通過這樣注解開發,可以代替配置xml的編寫。在下一節中將給出具體的應用案例。