Struts 2配置详解
學(xué)習(xí)內(nèi)容
?Struts 2配置文件
?Action的配置
?Result的配置
?屬性驅(qū)動與模型驅(qū)動
能力目標(biāo)
?熟練進(jìn)行Struts 2配置
?熟練使用屬性驅(qū)動和模型驅(qū)動
本章簡介
上一章我們初步學(xué)習(xí)了Struts 2框架,包括Struts 2體系結(jié)構(gòu)和運(yùn)行流程,并通過登錄案例介紹了使用Struts 2進(jìn)行開發(fā)的基本步驟。
本章將深入學(xué)習(xí)Struts 2框架,主要內(nèi)容是Struts 2的配置文件,包括Action的配置、Result的配置等等,只有掌握了配置文件,才能更好的使用和擴(kuò)展Struts 2的功能。
核心技能部分
4.1?Struts 2的配置文件
通過上一章的學(xué)習(xí),我們知道Struts 2框架默認(rèn)的配置文件是struts.xml,該文件通常放在WEB-INF\classes目錄下,該目錄下的struts.xml會自動被Struts 2框架加載。本節(jié)將詳細(xì)介紹該配置文件各元素的含義和使用。
4.1.1?中文亂碼處理
在Struts 2中,如果客戶端請求中包含有中文數(shù)據(jù),那就很容易出現(xiàn)中文亂碼問題。當(dāng)然我們會有很多方法來解決這個問題,在struts.xml中可以通過<constant>配置更簡單的解決中文亂碼問題,參考代碼如下所示。
<struts> <constant name="struts.i18n.encoding" value="utf-8"></constant> <package name="admin" extends="struts-default"> <action name="login" class="com.zy.AdminAction"> <result name="success" type="dispatcher">/index.jsp</result> <result name="error" type="redirect">/fail.jsp</result> </action> </package> </struts>在struts.xml中,<constant>元素用來配置常量,配置時必須指定兩個屬性:name和value。上述代碼中的struts.i18n.encoding是Struts 2中已經(jīng)存在的常量屬性,用來配置Web應(yīng)用程序默認(rèn)的編碼集,相當(dāng)于調(diào)用HttpServletRequset對象的setCharacterEncoding方法。
4.1.2?包配置
在Java項(xiàng)目中,類通常都需要“包(package)”來進(jìn)行組織和管理。同樣的道理,struts.xml中眾多的Action也需要進(jìn)行統(tǒng)一的組織和管理,所以Struts 2的配置文件也引入了“包(package)”,并通過“包(package)”來管理Action和攔截器。
在struts.xml文件中,package元素用來配置包,配置時通常需要指定以下三個屬性:
?name:必需屬性,指定包的名字,不允許重復(fù)。
extends:可選屬性,指定要繼承的包,可以繼承其它包中定義的action、攔截器等。
?namespace:可選屬性,定義該包的命名空間(命名空間將在下一節(jié)講述)。
<struts> <package name="admin" extends="struts-default"> <action name="login" class="com.zy.AdminAction"> <result name="success" type="dispatcher">/index.jsp</result> <result name="error" type="redirect">/fail.jsp</result> </action> </package> </struts>在上述代碼中,我們使用package元素配置了一個包,名字是“admin”,并繼承了“struts-default”包。
通過使用extends,你可以指定本package繼承另外一個package的所有的配置。當(dāng)某個package繼承了另外一個package的所有配置,那么你就無需對父package中已經(jīng)聲明過的配置定義做再次的定義。?
???同時,如果重復(fù)定義父package中已聲明過的配置定義,那么這些重復(fù)定義聲明將覆蓋父package中的相關(guān)定義。
上例中,我們讓admin包繼承了struts-default包,那么這個包又在哪里呢?struts-default是Struts 2默認(rèn)的包,該包定義在struts-defalut.xml文件中。該文件是Struts 2框架自帶的配置文件,為框架提供諸多默認(rèn)配置并在運(yùn)行時自動被加載。
現(xiàn)在我們打開struts2-core-2.1.8.1.jar,在jar包中可以找到struts-default.xml文件。打開struts-default.xml后,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?><struts> <bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" /> <bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" /> <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="xwork" class="com.opensymphony.xwork2.DefaultActionProxyFactory"/> <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/> <!-- 省略了其他的bean節(jié)點(diǎn)的定義 --> <!-- Only have static injections --> <bean class="com.opensymphony.xwork2.ObjectFactory" static="true" /> <bean class="com.opensymphony.xwork2.util.XWorkConverter" static="true" /> <!-- 省略了其他的靜態(tài)注入的定義 --> <package name="struts-default" abstract="true"> <result-types> <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <!-- 省略了其他的ResultType的定義 --> </result-types> <interceptors> <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/> <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/> <!-- 省略了其他的Interceptor的定義 --> <!-- Basic stack --> <interceptor-stack name="basicStack"> <interceptor-ref name="exception"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> </interceptor-stack> <!-- A complete stack with all the common interceptors in place. Generally, this stack should be the one you use, though it may do more than you need. Also, the ordering can be switched around (ex: if you wish to have your servlet-related objects applied before prepare() is called, you'd need to move servlet-config interceptor up. This stack also excludes from the normal validation and workflow the method names input, back, and cancel. These typically are associated with requests that should not be validated. --> <interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="i18n"/> <interceptor-ref name="chain"/> <interceptor-ref name="debugging"/> <interceptor-ref name="profiling"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack> <!-- 省略了其他的interceptor-stack節(jié)點(diǎn)的定義 --> </interceptors> <default-interceptor-ref name="defaultStack"/> </package> </struts>在上面的內(nèi)容中,我們看到定義了一個包?名字是struts-default,其中引用了一系列的系統(tǒng)攔截器,并定義了一些攔截器棧和默認(rèn)攔截器,admin包就是繼承了這個struts-default包,也就繼承到了里面的默認(rèn)攔截器的配置,通過第一章的學(xué)習(xí),我們知道action運(yùn)行前后會有一系列的攔截器自動運(yùn)行,也就是因?yàn)槔^承了struts-default包的緣故!
因此,正常情況下,自定義的action在配置的時候要確保直接或者間接的繼承到struts-default,否則action可能無法正常工作。因?yàn)閟truts2很多核心功能都是攔截來實(shí)現(xiàn)的,如,從請求中把請求參數(shù)封閉到action,文件上傳和數(shù)據(jù)驗(yàn)證等都是通過攔截器實(shí)現(xiàn)的,struts-default定義了這些攔截器和Result類型,可以這么說,當(dāng)包繼承了struts-default才能使用struts2提供的核心功能,
?4.1.3?命名空間配置
在實(shí)際應(yīng)用中,同一個struts.xml文件中可能會出現(xiàn)同名的Action,為了便于管理,Struts 2通過命名空間來區(qū)分同名Action,即Struts 2通過Action的邏輯名和其所在的命名空間來標(biāo)識一個Action。一個命名空間中不能存在同名的Action,不同的命名空間可以存在同名的Action。在struts.xml文件中,通過給包(package)指定namespace屬性來為Action設(shè)置命名空間。
<struts> <package name="admin" extends="struts-default" namespace="/admin"> <action name="query" class="com.zy.QueryAction"> <result name="success" type="dispatcher">/list.jsp</result> </action> </package> </struts>在上述代碼中,我們配置了一個名字是“admin”的包,并指定其命名空間為“/admin”,這時在訪問包中名字是“query”的action時,應(yīng)該這樣寫:
http://localhost:8080/web應(yīng)用名/admin/query.action
在URL中必須指明命名空間的名字,如果一個包沒有配置命名空間,那默認(rèn)為“”。此時就應(yīng)該這樣寫:
http://localhost:8080/web應(yīng)用名/query.action
4.1.4?包含配置
在實(shí)際應(yīng)用中,Struts 2配置文件可能會變的十分龐大,這不利于管理和維護(hù)。這時我們可以把一個Struts 2配置文件拆分成若干個配置文件,這樣也有利用團(tuán)隊(duì)協(xié)作開發(fā)。
<struts> <package name="admin" extends="struts-default"> <action name="login" class="com.zy.AdminAction"> <result name="success" type="dispatcher">/index.jsp</result> <result name="error" type="redirect">/fail.jsp</result> </action> <action name="query" class="com.zy.QueryAction"> <result name="success" type="dispatcher">/list.jsp</result> </action> <action name="add" class="com.zy.AddAction"> <result name="success" type="redirect">query.action</result> <result name="error" type="redirect">/error.jsp</result> </action> </package> </struts>上述代碼配置了三個Action,下面我們把這個配置文件拆分成兩個,分別是struts1.xml和struts2.xml,代碼如下所示。
struts1.xml
<struts> <package name="p1" extends="struts-default"> <action name="login" class="com.zy.AdminAction"> <result name="success" type="dispatcher">/index.jsp</result> <result name="error" type="redirect">/fail.jsp</result> </action> </package> </struts> struts2.xml <struts> <package name="p2" extends="struts-default"> <action name="query" class="com.zy.QueryAction"> <result name="success" type="dispatcher">/list.jsp</result> </action> <action name="add" class="com.zy.AddAction"> <result name="success" type="redirect">query.action</result> <result name="error" type="redirect">/error.jsp</result> </action> </package> </struts> 拆分之后,在struts.xml文件中通過include元素來包含struts1.xml和struts2.xml,代碼如下所示。 <struts> <include file="struts1.xml"/> <include file="struts2.xml"/> </struts>include元素的file屬性用來設(shè)置配置文件的路徑。
4.1.5?struts.properties
Struts 2框架有兩個配置文件,一個是我們前面常用的struts.xml,另一個是struts.properties。這個文件是struts2框架的全局屬性配置文件。
struts.properties是一個標(biāo)準(zhǔn)的Properties文件,該文件是由一系列的key-value(鍵值對)組成,每個key就是一個Struts 2的屬性,該key對應(yīng)的value就是一個Struts 2的屬性值。該文件通常放在Web應(yīng)用的WEB-INF/classes目錄下,Struts 2框架會自動加載這個文件。
下面將該文件常用的配置屬性詳細(xì)地列舉出來。
?struts.i18n.encoding
設(shè)置Web應(yīng)用的默認(rèn)編碼集。此屬性對于處理中文請求參數(shù)很有用,通常設(shè)置為GB2312或者utf-8。
?struts.multipart.parser
該屬性設(shè)置處理multipart/form-data的MIME類型請求的框架,該屬性支持cos、pell和jakarta等屬性值,即分別對應(yīng)使用cos的文件上傳框架、pell上傳及common-fileupload文件上傳框架,該屬性的默認(rèn)值為jakarta。文件上傳會在后面的章節(jié)中介紹。
?struts.multipart.saveDir
該屬性設(shè)置上傳文件的臨時保存路徑,該屬性的默認(rèn)值是javax.servlet.context.tempdir。
?struts.multipart.maxSize
該屬性設(shè)置Struts 2文件上傳中整個請求內(nèi)容允許的最大字節(jié)數(shù)。
struts.action.extension
該屬性設(shè)置需要Struts 2處理的請求后綴,該屬性的默認(rèn)值是action,即所有匹配*.action的請求都由Struts 2處理。如果用戶需要指定多個請求后綴,則多個后綴之間以英文逗號(,)隔開。
?struts.configuration.xml.reload
該屬性設(shè)置當(dāng)struts.xml文件改變后,系統(tǒng)是否自動重新加載該文件。該屬性的默認(rèn)值是false。
?struts.custom.i18n.resources
該屬性指定Struts 2應(yīng)用所需要的國際化資源文件,如果有多個國際化資源文件,則多個資源文件的文件名以英文逗號(,)隔開。
?struts.configuration.files
該屬性指定Struts 2框架默認(rèn)加載的配置文件,如果需要指定默認(rèn)加載多個配置文件,則多個配置文件的文件名之間以英文逗號(,)隔開。該屬性的默認(rèn)值為struts-default.xml,struts-plugin.xml,struts.xml,看到該屬性值后大家應(yīng)該明白為什么Struts 2框架會默認(rèn)加載struts.xml文件了。
struts.properties文件的內(nèi)容均可在struts.xml中以<constant>元素進(jìn)行配置。如果這兩個文件中的配置有沖突,那么將以struts.properties文件的內(nèi)容為準(zhǔn),這與struts的搜索順序有關(guān)。
sturt2中搜索加載常量的順序是:
struts-default.xml ????
struts-plugin.xml ?????
struts.xml ????????????
sturts.propreties ????
web.xml ???????????????
如果在struts.xml中做了主題的配置:
<constant name="struts.ui.theme" value="xhtml"></constant>
同時在struts.properties中做了主題的配置:
struts.ui.theme=simple
并使用struts2標(biāo)簽開發(fā)頁面如下:
<s:form action="">
???? <s:textfield name="uname" label="用戶名"></s:textfield>
???? <s:password name="paw" label="密碼"></s:password>
???? <s:submit value="登錄"></s:submit>
???? </s:form>
運(yùn)行程序效果如圖4.1.1所示:
?
圖4.1.1 主題配置
顯然?Struts.properties中的主題配置效力高于struts.xml。
4.2?Struts 2的Action
在使用Struts 2框架進(jìn)行開發(fā)時,開發(fā)者需要編寫大量的Action,這是應(yīng)用的核心。Action的主要作用有三個:
??封裝客戶端請求數(shù)據(jù)
??處理業(yè)務(wù)
??返回結(jié)果
在上一章我們說過Struts 2的Action可以不用繼承任何類和實(shí)現(xiàn)任何接口,它可以作為一個普通的JavaBean來使用。但是在實(shí)際應(yīng)用中,我們?nèi)孕枰恍┙涌诤皖悂砗喕疉ction。
1.?Action接口
為了使開發(fā)者編寫的Action更加規(guī)范,Struts 2提供了Action接口,下面是該接口的源代碼。
package com.opensymphony.xwork2; 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; }由上述代碼可知,Action接口中定義了五個字符串常量:SUCCESS、NONE、ERROR、INPUT、LOGIN。我們在execute方法中返回的字符串就來自這里。自定義的Action可以不實(shí)現(xiàn)該接口,開發(fā)者可以在execute方法中自定義返回字符串。
2.?ActionSupport類
我們自定義的Action通常繼承com.opensymphony.xwork2.ActionSupport類,此類實(shí)現(xiàn)了一些有用的接口(包括上面的Action接口),提供了國際化、數(shù)據(jù)驗(yàn)證等很多實(shí)用功能。該類的內(nèi)容會在后續(xù)章節(jié)中學(xué)習(xí)。
4.2.1?動態(tài)方法調(diào)用
在上一章的任務(wù)實(shí)訓(xùn)部分,我們使用Struts 2實(shí)現(xiàn)了對管理員的增、刪、改、查等操作。在實(shí)現(xiàn)過程中,每個請求都對應(yīng)一個Action,例如:添加管理員對應(yīng)的是AddAction,查詢管理員對應(yīng)的是QueryAction,刪除管理員對應(yīng)的是DelAction。在實(shí)際應(yīng)用中,類似的情況經(jīng)常出現(xiàn),即:隨著項(xiàng)目規(guī)模的擴(kuò)大,我們不得不管理大量的Action,Struts 2框架提供了動態(tài)方法調(diào)用來解決這個問題,也就是說我們可以在一個Action中自定義不同的方法來處理多個請求。
DMI(Dynamic Method Invocation)即動態(tài)方法調(diào)用,這時不能只請求某個Action,還要使用感嘆號(!)來標(biāo)識出要調(diào)用的方面名,語法如下所示:
語法
Action的邏輯名!Action中的方法名?.action
示例4.1
下面我們把上一章的實(shí)訓(xùn)任務(wù)通過動態(tài)方法調(diào)用進(jìn)行優(yōu)化,這時只需要一個Action,代碼如下所示:
public class AdminAction { private int id; private String logName; private String logPwd1; private AdminDao ad=new AdminDao(); public String addAdmin() { //添加管理員 if(ad.addAdmin(logName, logPwd1)) return "success"; else return "fail"; } public String queryAdmin() { //查詢管理員 List<Admin> adminList=ad.getAllAdmin(); Map req=(Map)ActionContext.getContext().get("request"); req.put("adminList", adminList); return "list"; } public String delAdmin(){ //刪除管理員 if(ad.delAdmin(id)) return "success"; else return "fail"; } //省略getter和setter方法 }在上述代碼中,AdminAction沒有繼承任何類和實(shí)現(xiàn)任何接口,我們在該類中自定義了三個方法分別實(shí)現(xiàn)對管理員的增加、刪除和查詢,返回字符串也是自定義的。
提示
Action中的自定義方法必須是無參的,返回類型必須是String
下面是struts.xml的代碼,這時的配置文件就精簡了許多。
<struts> <constant name="struts.i18n.encoding" value="utf-8"></constant> <package name="admin" extends="struts-default"> <action name="admin" class="com.zy.AdminAction"> <resultname="success" type="redirect">admin!queryAdmin.action</result> <result name="fail" type="redirect">/error.jsp</result> <result name="list" type="dispatcher">/list.jsp</result> </action> </package> </struts> addAdmin.html視圖頁面的表單修改如下,其他代碼不變。 <form action="admin!addAdmin.action" method="post"> ... ... </form>list.jsp視圖頁面的【刪除】超鏈接修改如下,其他代碼不變。
<a href="admin!delAdmin.action?id=<%=admin.getId() %>">刪除</a>
?通過動態(tài)方法調(diào)用優(yōu)化后的項(xiàng)目更加精煉,方便以后的維護(hù)。?
4.2.2?method屬性
DMI在實(shí)際應(yīng)用中可能會出現(xiàn)安全隱患,因?yàn)橐坏┠橙酥懒薃ction的名字和其中的方法名,他就可以通過URL進(jìn)行任意的調(diào)用。Struts 2框架提供了一種更加安全的方式來實(shí)現(xiàn)DMI,在配置Action時可以通過method屬性來指定需要調(diào)用的方法。
示例4.2
下面我們通過method屬性來修改示例4.1,struts.xml代碼如下所示:
<struts> <constant name="struts.i18n.encoding" value="utf-8"></constant> <package name="admin" extends="struts-default"> <action name="query" class="com.zy.AdminAction" method="queryAdmin"> <result name="fail" type="redirect">/error.jsp</result> <result name="list" type="dispatcher">/list.jsp</result> </action> <action name="add" class="com.zy.AdminAction" method="addAdmin"> <result name="success" type="redirect">query.action</result> <result name="fail" type="redirect">/error.jsp</result> </action> <action name="del" class="com.zy.AdminAction" method="delAdmin"> <result name="success" type="redirect">query.action</result> <result name="fail" type="redirect">/error.jsp</result> </action> </package> </struts>在上述代碼中,我們配置了三個<action>,并分別設(shè)置了method屬性,屬性的值就是Action中的方法名。雖然通過method屬性實(shí)現(xiàn)動態(tài)方法調(diào)用可以避免安全隱患,但是這種方法會導(dǎo)致struts.xml中的<action>越來越多,不利于管理和維護(hù),為了解決這個問題我們可以使用通配符。
4.2.3?通配符的使用
在struts.xml文件中可以通過通配符簡化<action>的配置,<action>元素的name屬性支持通配符,通配符用星號(*)表示,用于匹配0到多個字符串。
示例4.3
下面我們把示例4.2通過通配符進(jìn)行優(yōu)化,代碼如下所示。
<struts> <package name="admin" extends="struts-default"> <action name="*" class="com.zy.AdminAction" method="{1}"> <result name="success" type="redirect">queryAdmin.action</result> <result name="fail" type="redirect">/error.jsp</result> <result name="list" type="dispatcher">/list.jsp</result> </action> </package> </struts>name屬性的值是“*”,表示允許這個Action可以匹配任何以“.action”結(jié)束的URL。method屬性的值是“{1}”,表示該屬性的值是name屬性值中的第一個“*”。例如:
http://localhost:8080/Struts6/queryAdmin.action
當(dāng)使用上面的URL請求時,“*”和“{1}”就變成了“queryAdmin”,正好是Action中實(shí)現(xiàn)添加管理員的方法名。
http://localhost:8080/Struts6/delAdmin.action?id=40
當(dāng)使用上面的URL請求時,“*”和“{1}”就變成了“delAdmin”,正好是Action中實(shí)現(xiàn)刪除管理員的方法名。
通過通配符實(shí)現(xiàn)動態(tài)方法調(diào)用不僅避免了安全隱患,還簡化了struts.xml的配置,建議在實(shí)際應(yīng)用中使用這種方法進(jìn)行開發(fā)。
4.2.4?配置默認(rèn)Action
如果請求的Action不存在,那么頁面上可能會呈現(xiàn)HTTP 404錯誤,為了避免這種情況的發(fā)生,Struts 2框架可以指定一個默認(rèn)的Action,如果沒有一個Action匹配客戶端請求,那么這個默認(rèn)的Action就會被執(zhí)行。
在struts.xml中,通過<default-action-ref />元素來配置默認(rèn)的Action。每個<default-action-ref />元素配置一個默認(rèn)的Action。
<struts> <package name="admin" extends="struts-default"> <action name="defaultAction"> <result>/error.jsp</result> </action> ... ... <default-action-ref name="defaultAction"/> </package> </struts>如果action元素的class屬性省略了,表示將使用默認(rèn)的ActionSupport類。?
4.3?Result配置
Struts 2的Action處理完用戶請求后會返回一個字符串,該字符串表示邏輯視圖名。struts.xml文件通過<result>元素配置邏輯視圖名,并實(shí)現(xiàn)與物理視圖資源的映射。<result>元素的配置包含兩部分:由name屬性設(shè)置邏輯視圖名,由type屬性設(shè)置視圖類型。
常用的結(jié)果類型有dispatcher類型和redirect類型。
??dispatcher類型相當(dāng)于“轉(zhuǎn)發(fā)”,request、session等對象都會被轉(zhuǎn)發(fā)到視圖頁面。
??redirect類型相當(dāng)于“重定向”,將會丟失request、session等對象。
如果不設(shè)置type屬性,默認(rèn)的類型是dispatcher。
4.3.1?動態(tài)結(jié)果
在某些情況下,事先并不能確定使用哪個結(jié)果視圖,必須在程序運(yùn)行期間才能確定,這時如何在struts.xml中進(jìn)行配置呢?我們可以在配置時使用表達(dá)式,在程序運(yùn)行時,由框架根據(jù)表達(dá)式的值來確定要使用哪個結(jié)果視圖,這就是動態(tài)結(jié)果。
下面我們通過一個案例來演示動態(tài)結(jié)果的用法,完善登錄案例,如果是普通用戶就跳轉(zhuǎn)到user.jsp,如果是管理員就跳轉(zhuǎn)到admin.jsp。用戶的身份必須在程序運(yùn)行過程中才能確定。
示例4.4
需要在原來的Action中增加一個屬性用來標(biāo)識用戶的身份,并提供getter/setter方法,代碼如下所示。
public class AdminAction { //省略其他屬性 private String flag; public String login() { if(user.isAdmin()) flag="admin"; else flag="user"; return "success"; } public String getFlag() { return flag; } public void setFlag(String flag) { this.flag = flag; }//省略其他業(yè)務(wù)方法//省略其他getter和setter方法 }如果是普通用戶,那么flag屬性的值被設(shè)置為“user”,如果是管理員,那么flag屬性的值被設(shè)置為“admin”。
下面我們看一下在struts.xml文件中如何配置,代碼如下所示。
<struts> <package name="admin" extends="struts-default"> <action name="login" class="com.zy.AdminAction"> <result name="success" type="dispatcher">/${flag}.jsp</result> <result name="error" type="redirect">/fail.jsp</result> </action> </package> </struts>注意上述代碼中的加粗部分,我們把視圖頁面的名字用“${ flag }”代替,“${ flag }”表達(dá)式可以獲得Action中flag屬性的值。在程序運(yùn)行時,如果是管理員登錄,該表達(dá)式的值為admin,即跳轉(zhuǎn)到admin.jsp;如圖是普通用戶登錄,該表達(dá)式的值為user,即跳轉(zhuǎn)到user.jsp。
4.3.2?全局結(jié)果
在之前的項(xiàng)目中,我們都是把<result>元素配置到了<action>元素內(nèi)部,這些<result>不能被其他Action使用。但是某些<result>可能是公用的,即多個Action都需要同一個<result>,這時我們可以通過配置全局結(jié)果來滿足需求。
全局結(jié)果定義在包(package)中,而不是某個<action>元素內(nèi)部,包(package)中的所有Action都可以使用這個結(jié)果。
<struts> <package name="admin" extends="struts-default"> <global-results> <result name="error" type="redirect">/error.jsp</result> </global-results> <action name="login" class="com.zy.AdminAction"> <result name="success" type="dispatcher">/index.jsp</result> </action> </package> </struts>在struts.xml文件中使用<global-results>元素配置全局結(jié)果,可以配置多個全局結(jié)果。
?
4.4?屬性驅(qū)動與模型驅(qū)動
4.4.1?屬性驅(qū)動
在Struts 1中,ActionForm用來封裝客戶端請求數(shù)據(jù)。在Struts 2中,沒有了ActionForm,使用Action就可以封裝客戶端請求數(shù)據(jù),例如登錄案例中的Action,代碼如下所示。
public class AdminAction extends ActionSupport { private int id; private String name; private String pwd; public String execute() { } //省略getter和setter方法 } 與之對應(yīng)的表單如下所示。 <form action="login.action" method="post"><table width="397" height="169" border="0" align="center"><tr><td colspan="2" align="center">管理員登錄</td></tr><tr><td width="163" align="right">登錄名稱:</td><td width="218"><input name="name" type="text" class="txt"></td></tr><tr><td align="right">登錄密碼:</td><td><input name="pwd" type="password" class="txt"></td></tr><tr><td colspan="2" align="center"><input type="submit" value="提交"></td></tr> </table> </form>注意上述代碼中的加粗部分,表單中每一個元素的name都對應(yīng)Action中的一個屬性,程序在運(yùn)行時,Struts 2通過Action的屬性來封裝客戶端請求數(shù)據(jù),這種方式稱之為屬性驅(qū)動。我們前面所編寫的案例采用的都是這種方式。
4.4.2?模型驅(qū)動
屬性驅(qū)動使得Action既要封裝客戶端請求數(shù)據(jù),還要處理業(yè)務(wù),這就造成Action承擔(dān)了過多的職責(zé),分工也不夠清晰。更好的解決辦法就是采用單獨(dú)的Model(模型)來封裝請求數(shù)據(jù),這就是模型驅(qū)動。模型驅(qū)動中的Model(模型)其實(shí)就是一個實(shí)體類或POJO,專門用來封裝客戶端請求數(shù)據(jù)。
示例4.5
下面我們把前面的登錄案例改成模型驅(qū)動。首先需要一個封裝登錄表單的實(shí)體類Admin作為模型,代碼如下所示。
public class Admin {
private String name; ?//登錄名稱
private String pwd; ??//登錄密碼
//省略getter和setter方法
}
Action就不需要分散的屬性來封裝表單數(shù)據(jù)了,只需要一個Admin類型的屬性即可,代碼如下所示。
public class LoginAction extends ActionSupport { private Admin admin; public String execute() { AdminDao ad=new AdminDao(); if(ad.checkLogin(admin.getName(), admin.getPwd())) return SUCCESS; else return ERROR; } public Admin getAdmin() { return admin; } public void setAdmin(Admin admin) { this.admin = admin; } } 與之對應(yīng)的表單需要進(jìn)行如下修改。 <form action="login.action" method="post"><table width="397" height="169" border="0" align="center"><tr><td colspan="2" align="center">管理員登錄</td></tr><tr><td width="163" align="right">登錄名稱:</td><td width="218"><input name="admin.name" type="text" class="txt"></td></tr><tr><td align="right">登錄密碼:</td><td><input name="admin.pwd" type="password" class="txt"></td></tr><tr><td colspan="2" align="center"><input type="submit" value="提交"></td></tr> </table> </form>注意上述代碼中的加粗部分,在使用模型驅(qū)動時,表單元素name屬性的值由兩部分組成,第一部分是Action中模型對象的名字,第二部分是模型對象的屬性名。如果在實(shí)際應(yīng)用中需要在頁面上輸出模型對象中屬性的值,則可以使用“${ admin.name }”進(jìn)行輸出。
本章總結(jié)
?Struts 2的配置文件
(1)中文亂碼處理??梢酝ㄟ^名為struts.i18n.encoding的常量配置字符集。
(2)包配置。包的常用屬性有name, extends和namaspaces。
(3)包含配置。
?Struts 2的Action
(1)動態(tài)方法調(diào)用。
(2)Method參數(shù)和通配符的使用。
Result配置
屬性驅(qū)動和模型驅(qū)動
任務(wù)實(shí)訓(xùn)部分
1:查詢圖書
訓(xùn)練技能點(diǎn)
??method屬性
??通配符
??需求說明
在第二章的核心任務(wù)部分我們講解了一個查詢圖書的案例,現(xiàn)在要求使用Struts 2進(jìn)行
重構(gòu),并應(yīng)用method屬性和通配符進(jìn)行優(yōu)化處理
??實(shí)現(xiàn)步驟
(1)?創(chuàng)建對應(yīng)圖書表的實(shí)體類Book.java
(2)?創(chuàng)建實(shí)現(xiàn)數(shù)據(jù)庫連接和關(guān)閉的工廠類DaoFactory.java
(3)?創(chuàng)建實(shí)現(xiàn)圖書查詢功能的Dao類BookDao.java
(4)?創(chuàng)建實(shí)現(xiàn)圖書查詢業(yè)務(wù)的Action類QueryAction.java,參考代碼如下所示。
public class QueryAction{ private String keywords; private BookDao bd=new BookDao(); private List bookList=new ArrayList(); private ActionContext ac=ActionContext.getContext(); private Map req=(Map)ac.get("request"); public String queryByName() //按書名查詢 { bookList=bd.getBooks("name", keywords); req.put("bookList", bookList); return "success"; } public String queryByAuthor() //按作者查詢 { bookList=bd.getBooks("author", keywords); req.put("bookList", bookList); return "success"; } public String queryByPublisher() //按出版社查詢 { bookList=bd.getBooks("publish", keywords); req.put("bookList", bookList); return "success"; } //省略getter和setter方法 }(5)?創(chuàng)建實(shí)現(xiàn)查詢界面的視圖query.html,參考代碼如下所示。 ?
<body><form name="f" method="post"><table width="330" height="94" border="0" align="center"><tr><td align="center">關(guān)鍵字:<input type="text" name="keywords"></td></tr><tr><td align="center"><input name="rd" type="radio" value="queryByName" checkedοnclick="change(this.value)">按書名查詢<input name="rd" type="radio" value="queryByAuthor"οnclick="change(this.value)">按作者查詢<input name="rd" type="radio" value="queryByPublisher"οnclick="change(this.value)">按出版社查詢</td></tr><tr><td align="center"><input type="button" value="查詢"οnclick="submitForm()"></td></tr></table></form></body> <script> var v="queryByName"; function change(val) { v=val; } function submitForm() { f.action=v+".action"; f.submit(); } </script>由于本案例要使用動態(tài)方法調(diào)用,所以在該頁面我們需要使用JS動態(tài)合成請求URL,運(yùn)行效果如圖4.2.1所示。
圖4.2.1 查詢界面
(6)?編輯struts.xml,參考代碼如下所示。
<struts> <constant name="struts.i18n.encoding" value="utf-8"></constant> <package name="book" extends="struts-default"> <action name="*" class="com.zy.QueryAction" method="{1}"> <result name="success" type="dispatcher">/list.jsp</result> </action> </package> </struts>(7)?顯示查詢結(jié)果的視圖頁面list.jsp這里不再多述,運(yùn)行效果如圖4.2.2所示。
?
圖4.2.2 查詢結(jié)果
2:實(shí)現(xiàn)簡易計(jì)算器
訓(xùn)練技能點(diǎn)
?動態(tài)方法調(diào)用
?模型驅(qū)動
??需求說明
使用動態(tài)方法調(diào)用和模型驅(qū)動優(yōu)化上一章的實(shí)訓(xùn)任務(wù)4
??實(shí)現(xiàn)步驟
(1)?創(chuàng)建實(shí)體類Calculator.java,參考代碼如下所示。
public class Calculator { private double num1; private double num2; private double result;//省略getter和setter方法 }(2)?創(chuàng)建Action類CalculatorAction.java,參考代碼如下所示。
public class CalculatorAction { private Calculator cal; public String jia() //加法運(yùn)算 { cal.setResult(cal.getNum1()+cal.getNum2()); return "result"; } public String jian() //減法運(yùn)算 { cal.setResult(cal.getNum1()-cal.getNum2()); return "result"; } public String cheng() //乘法運(yùn)算 { cal.setResult(cal.getNum1()*cal.getNum2()); return "result"; } public String chu() //除法運(yùn)算 { cal.setResult(cal.getNum1()/cal.getNum2()); return "result"; } //省略getter和sertter方法 }(3)?創(chuàng)建計(jì)算器的視圖頁面,參考代碼如下所示。
<form name="frm" method="post"><table width="298" border="0" align="center"><tr><td colspan="2" align="center">簡易計(jì)算器</td></tr><tr><td width="76" align="center">第一個數(shù)</td><td width="206"><input type="text" name="cal.num1"value="${cal.num1}"></td></tr><tr><td align="center">第二個數(shù)</td><td><input type="text" name="cal.num2" value="${cal.num2}"></td></tr><tr><td colspan="2" align="center"><input type="button" value="+" οnclick="submitForm('jia')"><input type="button" value="-" οnclick="submitForm('jian')"><input type="button" value="*" οnclick="submitForm('cheng')"><input type="button" value="/" οnclick="submitForm('chu')"></td></tr><tr><td align="center">結(jié)果</td><td><input type="text" name="result" readonlyvalue="${cal.result}"></td></tr></table> </form></body> <script> function submitForm(op) { frm.action="cal_"+op+".action"; frm.submit(); } </script>由于要使用動態(tài)方法調(diào)用和通配符,所以在頁面中我們使用JS合成請求URL。同時我們使用EL表達(dá)式輸出了Action中屬性的值。
(4)?配置struts.xml,代碼如下所示。
<package name="calculator" extends="struts-default"> <action name="cal_*" class="com.test.CalculatorAction" method="{1}"> <result name="result" type="dispatcher">/calculator.jsp</result> </action> </package>圖4.2.3 計(jì)算器
3:模型驅(qū)動
訓(xùn)練技能點(diǎn)
模型驅(qū)動
??需求說明
把上一章的前三個實(shí)訓(xùn)任務(wù)使用模型驅(qū)動進(jìn)行優(yōu)化
鞏固練習(xí)
一、選擇題
1. 以下關(guān)于<action>元素的說法錯誤的是()。
A.?name屬性是必須的
B.?name屬性不能唯一確定一個Action
C.?method屬性表示調(diào)用Action中的哪個方法
D.?通配符除了星號(*)還有下劃線(_)
2. 以下關(guān)于<result>元素的說法錯誤的是()。
A. ?type屬性指定結(jié)果類型
B. ?name屬性指定結(jié)果的邏輯名
C. ?name屬性的值必須是Action接口中的某個常量
D. 默認(rèn)的類型是dispatcher
3. Struts 2的結(jié)果類型有()。
A. ?dispatcher
B.?redirect
C. ?chain
D. ?success
4. DMI的正確寫法是()。
A.?Action的邏輯名!Action中的方法名?.action
B.?Action的邏輯名_Action中的方法名?.action
C.?Action的邏輯名!Action中的方法名?.do
D.?Action的邏輯名_Action中的方法名?.do
5. 以下關(guān)于Struts 2配置文件說法正確的是()。
A.?struts.properties是必須的配置文件
E.?struts.properties通過標(biāo)簽元素進(jìn)行配置
F.?只能同時使用struts.properties與struts.xml中的一個
G.?struts.properties中的配置通常都可以在struts.xml中進(jìn)行配置?
二、上機(jī)練習(xí)
?把上一章的課后上機(jī)練習(xí)使用模型驅(qū)動和動態(tài)方法調(diào)用進(jìn)行重構(gòu)。運(yùn)行效果圖如下。?
圖4.3.1 增加【修改】超鏈接
?
圖4.3.2 密碼修改頁面
?
總結(jié)
以上是生活随笔為你收集整理的Struts 2配置详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Struts 2基础入门
- 下一篇: Struts2标签库和OGNL