Result Types
從action生成結(jié)果,并返回組用戶(hù)不同的結(jié)果值,不都需要相同的類(lèi)型.結(jié)果"success"可以渲染為一JSP頁(yè)面,但結(jié)果"error"可能需要發(fā)送一個(gè)HTTP頭返回給瀏覽器.
結(jié)果的類(lèi)型使用"type"屬性在結(jié)果節(jié)點(diǎn)配置.與"name"屬性相似,這個(gè)屬性也有一個(gè)默認(rèn)值-"dispatcher"-將渲染JSPs.大多數(shù)的時(shí)間,你將使用所提供的結(jié)果類(lèi)型,但有時(shí)也可能提供自定義的實(shí)現(xiàn).
Request and Form Data
為了要做出Action應(yīng)該如何工作的決定,并且提供數(shù)據(jù)的數(shù)據(jù)庫(kù)持久對(duì)象,Action可能需要從請(qǐng)求字符串中訪問(wèn)值,并生成數(shù)據(jù).
Struts2沿序JavaBean的方式-如果你想訪問(wèn)數(shù)據(jù),你需要為字段提供一個(gè)getter和setter方法.訪問(wèn)請(qǐng)求字符串和form里的值是一樣的.每個(gè)請(qǐng)求字符串或是Form里的值都是一個(gè)簡(jiǎn)單的名值對(duì),所以為一個(gè)特定的名稱(chēng)賦值,需在action里創(chuàng)建一個(gè)setter方法.例如,如果JSP發(fā)起了一個(gè)請(qǐng)求,"/home.action?framework=struts&version=2",Action需要提供setter方法"setFramework(String frameworkname)",和setter方法"setVersion(int version)".
注意在這個(gè)例子中,setter方法不是一定需要一個(gè)String的值.默認(rèn)的,Struts2將從一個(gè)String轉(zhuǎn)換成action所需的類(lèi)型.可以轉(zhuǎn)換所有的原始類(lèi)型和基礎(chǔ)對(duì)象類(lèi)型,而且可以配置你自己的自定義類(lèi).Struts2同樣可以操作復(fù)雜對(duì)象中的值,例如,一個(gè)名稱(chēng)在Form元素中稱(chēng)為"person.address.home.postcode",值為"2",Struts2將使用同樣的調(diào)用方式"getPerson().getAddress().getHome().setPostcode(2)".
Accessing Business Services
到現(xiàn)在為止,我們都在討論如果配置action,如果控制渲染不同的結(jié)果返回給用戶(hù).對(duì)于action是做什么的,是非常重要的一部分,但是,返回它們的結(jié)果之前,一些處理需要執(zhí)行.對(duì)于這個(gè),它們需要訪問(wèn)一些不同類(lèi)型的對(duì)象-商業(yè)對(duì)象,數(shù)據(jù)訪問(wèn)對(duì)象,或是其他資源.
為了提供一個(gè)松耦合的系統(tǒng),Struts2使用稱(chēng)為依賴(lài)注入或是控制反轉(zhuǎn)(IOC)的技術(shù).依賴(lài)注入可以通過(guò)構(gòu)造函數(shù)注入,接口注入和set方法注入來(lái)實(shí)現(xiàn).Struts2使用set方法注入.意思是對(duì)于action可用的對(duì)象,你僅需要提供一個(gè)setter.首選的依賴(lài)注入框架是Spring框架,可以通過(guò)插件配置進(jìn)來(lái).另一個(gè)選擇是Plexus,或都如果你喜歡,你可以替換成你自己的實(shí)現(xiàn).
也有一些對(duì)象不能用Spring框架管理,比如像HttpServletRequest.這些對(duì)象是組合使用setter注入和接口注入來(lái)操作.對(duì)于每一個(gè)非商業(yè)對(duì)象,都有個(gè)相應(yīng)的口(大家都知道的"aware"接口),Action必須實(shí)現(xiàn)這個(gè)接口.
注:
WebWork最初有自己的依賴(lài)注入框架.它在2.2版時(shí),刪除了這個(gè)特征,而改用Spring框架替換.最初的組件框架是基于接口的,所以為每個(gè)組件接口和接口的實(shí)現(xiàn)類(lèi),都需要提供.
另外,每個(gè)組件都有一個(gè)"Aware"接口,為組件提供一個(gè)setter.如果接口是"UserDAO",那么Aware接口則為"UserDAOAware"(約定俗成),并且擁有一個(gè)方法-一個(gè)setter,"void setUserDAO(UserDAO dao);".
攔截器會(huì)為必要的接口和setters注入必要的對(duì)象注入.
Accessing Data from the Action
有些時(shí)候,需要查看被action修改過(guò)的對(duì)象.有幾種技術(shù)可以被使用.
很多WEB程序員熟悉的技術(shù)是將需要訪問(wèn)的對(duì)象放到HttpServletRequest和HttpSession中.這可以通過(guò)實(shí)現(xiàn)"aware"接口來(lái)完成,然后設(shè)置對(duì)像,使用特定的名稱(chēng)訪問(wèn).
如果你想使用內(nèi)建的標(biāo)簽或是引入JSTL支持,訪問(wèn)數(shù)據(jù)就非常容易了.他們兩個(gè)都可以通過(guò)值棧直接存取action.唯一的工作就是程序員需要給Action里允許被訪問(wèn)的需要存取的對(duì)象提供getter方法.
我們將在后面的部分更詳細(xì)的討論值棧.
Interceptors
Struts2框架使用攔截器提供了許多特征;例如包含異常處理,文件上傳,生命周期回調(diào)和驗(yàn)證.攔截器是同Servlet過(guò)濾器或是JDKs的代理類(lèi)相同的概念.它們提供了Action的前處理和后處理的方法.與Servlet過(guò)濾器相似,攔截器也可以分層和排序.They have access to the action being executed, as well as all environmental variables and execution properties.
讓我們開(kāi)始討論攔截器依賴(lài)注入.依賴(lài)注入到action當(dāng)中,與我們之前看到的一樣,有兩種不同的形式出現(xiàn).有一些攔截器是我們已經(jīng)提過(guò)的:
Spring框架-ActionAutowiringInterceptor攔截器
請(qǐng)求字符串和Form值-ParametersInterceptor攔截器
Servlet基礎(chǔ)對(duì)象-ServletConfigInterceptor
前兩個(gè)攔截器獨(dú)立工作,不需要Action再做什么,但最后一個(gè)卻是不同的.它同下面的接口協(xié)作工作:
SessionAware-通過(guò)一個(gè)Map來(lái)提供對(duì)所有Session屬性的訪問(wèn)
ServletRequestAware-提供對(duì)HttpServletRequest對(duì)象的訪問(wèn)
RequestAware-通過(guò)一個(gè)Map來(lái)提供對(duì)所有Request屬性的訪問(wèn)
ApplicationAware-通過(guò)一個(gè)Map來(lái)提供對(duì)所有Application屬性的訪問(wèn)
ServletResponseAware-提供對(duì)ServletResponse對(duì)象的訪問(wèn)
ParameterAware-通過(guò)一個(gè)Map來(lái)提供對(duì)所有請(qǐng)求字符串和Form值的訪問(wèn)
PrincipalAware-提供對(duì)PrincipleProxy對(duì)象的訪問(wèn);這個(gè)對(duì)象實(shí)現(xiàn)了HttpServletRequest對(duì)象的規(guī)則和角色的方法, 但提供個(gè)代理,允許獨(dú)立于Action的實(shí)現(xiàn)
ServletContextAware-提供對(duì)ServletContext對(duì)象的訪問(wèn)
為了將正確的數(shù)據(jù)注入到Action當(dāng)中,它需要實(shí)現(xiàn)必需的接口.
Configuration
如果我們想要能夠注入(或任何其他由攔截器提供的功能)到我們的Action,我們需要提供配置.像其他元素一樣,大多數(shù)的攔截器已經(jīng)為你配置好了.只需要你的Actions的包繼承自"struts-default"包即可.
要配置一個(gè)新的攔截器,我們首先需要定義這個(gè)攔截器.將<interceptors … />和<interceptor …/>標(biāo)簽直屬<package>標(biāo)簽.對(duì)于上面提到的Spring框架攔截器,它的配置如下:
1
<interceptors>
2
…
3
<interceptor name="autowiring"
4
class="interceptor.ActionAutowiringInterceptor"/>
5
</interceptors>
我們同樣需要確認(rèn)將攔截器應(yīng)用到需要它的Action上.這可以通過(guò)兩種方法實(shí)現(xiàn).第一種是將攔截器分配給每個(gè)Action:
1
<action name="my" class="com.fdar.infoq.MyAction" >
2
<result>view.jsp</result>
3
<interceptor-ref name="autowiring"/>
4
</action>
使用這種配置,可以被應(yīng)用到Action上的攔截器沒(méi)有數(shù)量的限制.現(xiàn)在所需要的就是排列攔截器的順序,它們都將被執(zhí)行.
第二種方式是為當(dāng)前的包分配默認(rèn)的攔截器:
<default-interceptor-ref name="autowiring"/>
這個(gè)定義是直屬<package ... />標(biāo)簽 的,并且只能有一個(gè)攔截器被定義成默認(rèn)的.
現(xiàn)在已將攔截器配置到了Action映射上了,它將在每個(gè)映射的URL請(qǐng)求時(shí)被執(zhí)行.但是這樣有一些限制,更多的時(shí)候,我們需要將多個(gè)攔截器指派給一個(gè)Action.事實(shí)上,Struts2里基礎(chǔ)的功能攔截器有很多,給每個(gè)Action指派7,8個(gè)攔截器也不是不可能的事.你可以假設(shè)一下,為每個(gè)Action配置各個(gè)攔截器,很快就會(huì)變得極難管理.因?yàn)檫@個(gè)原因,可使用攔截器棧來(lái)管理攔截器.這里有個(gè)例子,來(lái)自struts-default.xml文件:
1
<interceptor-stack name="basicStack">
2
<interceptor-ref name="exception"/>
3
<interceptor-ref name="servlet-config"/>
4
<interceptor-ref name="prepare"/>
5
<interceptor-ref name="checkbox"/>
6
<interceptor-ref name="params"/>
7
<interceptor-ref name="conversionError"/>
8
</interceptor-stack>
這個(gè)配置節(jié)點(diǎn)在<package ... />節(jié)點(diǎn)之下.每個(gè)<interceptor-ref … />標(biāo)簽引用一個(gè)攔截器或是一個(gè)在當(dāng)前攔截器棧之前定義的攔截器棧.
我們已經(jīng)看過(guò)如何將攔截器應(yīng)用于Action之上,應(yīng)用攔截器棧沒(méi)有什么不同.事實(shí)上,我們正是使用相同的標(biāo)簽:
1
<action name="my" class="com.fdar.infoq.MyAction" >
2
<result>view.jsp</result>
3
<interceptor-ref name="basicStack"/>
4
</action>
這種做法對(duì)于默認(rèn)的攔截器同樣有效-簡(jiǎn)單的使用一個(gè)攔截器棧要優(yōu)于使用單個(gè)的攔截器.
<default-interceptor-ref name="basicStack"/>
所以在配置實(shí)始攔截器和攔截器棧的時(shí)候,保證配置所有的攔截器和攔截器棧的名稱(chēng)唯一是非常重要的.
Implementing Interceptors
在你的應(yīng)用中使用自定義的攔截器為應(yīng)用提供cross-cutting特征是一個(gè)非常好的方式.只需要實(shí)現(xiàn)來(lái)自XWork框架的一個(gè)簡(jiǎn)單的接口.它只有3個(gè)方法:
1
public interface Interceptor extends Serializable
{
2
void destroy();
3
void init();
4
String intercept(ActionInvocation invocation) throws Exception;
5
}
事實(shí)上,如果實(shí)始化和清除方法不是必需的,可以替代成擴(kuò)展AbstractInterceptor類(lèi).這個(gè)類(lèi)為"destroy"和"init"方法提供了默認(rèn)的空實(shí)現(xiàn).
ActionInvocation對(duì)象提供了對(duì)運(yùn)行環(huán)境的訪問(wèn).它允許訪問(wèn)Action本身,context(對(duì)于一個(gè)WEB應(yīng)用,包含請(qǐng)求參數(shù),session參數(shù)等),action執(zhí)行的結(jié)果和調(diào)用action的方法,并確定action是否已經(jīng)被調(diào)用.
我們已經(jīng)知道如何來(lái)配置攔截器了,配置自定義攔截的方式完全相同.如果你創(chuàng)建了你自己的攔截器,你將同樣會(huì)考慮創(chuàng)建自定義的攔截器棧.這樣你將確認(rèn)你的應(yīng)用保持一致,新的攔截器可以處理所有需要他的actions.
Value Stack / OGNL
這個(gè)部分包括相關(guān)緊密地的二個(gè)概念.值棧正確的說(shuō)應(yīng)該是棧對(duì)象.OGNL則是Object Graph Navigational Language的縮寫(xiě),提供在值棧里標(biāo)準(zhǔn)的訪問(wèn)對(duì)象的方式.
值棧由下面羅列的對(duì)像組成:
1.Temporary Objects-在執(zhí)行期間創(chuàng)建臨時(shí)對(duì)象,并將其放入值棧;一個(gè)例子是將當(dāng)前集合中迭代的值循環(huán)的放到JSP標(biāo)簽中
2.The Model Object-如果模型對(duì)象正在使用,那么當(dāng)前模型在action之前放入值棧
3.The Action Object-正在被執(zhí)行的Action
4.Named Objects-這些對(duì)象包括#application,#session,#request,#attr和#parameters還有相應(yīng)的Servlet范圍
訪問(wèn)值棧可以通過(guò)許多不同的方式完成.大多數(shù)常用的方式是通過(guò)JSP提供的標(biāo)簽,Velocity和Freemarker.HTML標(biāo)簽一般被用于訪問(wèn)值棧中對(duì)象的屬性;控制標(biāo)簽(像if, elseif和iterator)用于表達(dá)式;數(shù)據(jù)標(biāo)簽可以操作棧本身(通過(guò)set和push).
在使用值棧的時(shí)候,不需要知道目標(biāo)對(duì)象存在于哪個(gè)范圍之內(nèi).如果你想取屬性"name"的值,你可以向值棧查詢(xún)這個(gè)屬性.每個(gè)棧元素,按照提供的順序,被詢(xún)問(wèn)它是否存在property.如果存在,則返回這個(gè)值.如果不存在,則下一個(gè)元素會(huì)被查詢(xún).這將繼續(xù)一直到棧里的元素全被查詢(xún).這是一個(gè)非常好的特征,你不需要知道這個(gè)值是哪里的-action,model或是HTTP請(qǐng)求-你只需要知道如果這個(gè)值存在,它就將被返回.
這是downside.如果property是常用的(例如"id"),而且你想從特定的對(duì)象(比方說(shuō)action)取值,也就是說(shuō)不是值棧中第一個(gè)遇到這個(gè)property的對(duì)象,返回的值可能與你的預(yù)期值不同.想要返回一個(gè)"id"的值,但它可能來(lái)自于JSP標(biāo)簽,臨時(shí)對(duì)象,或是來(lái)自模型對(duì)象.OGNL正好有一種訪問(wèn)對(duì)象的屬性的方法,我們可以在這里使用對(duì)我們有利的.如果我們知道棧在action中的深度,我們可以使用"[2].id"來(lái)替換"id".
事實(shí)上,OGNL是一種非常有特色的表達(dá)式語(yǔ)言.使用圓點(diǎn)符號(hào)對(duì)對(duì)象進(jìn)行導(dǎo)航(例如,使用表達(dá)式"person.address"來(lái)替代"getPerson().getAddress()"),OGNL支持的特征如類(lèi)型轉(zhuǎn)換,方法調(diào)用,集合的處理和創(chuàng)建,projection across ollections,表達(dá)式賦值和lambda表達(dá)式.完整的語(yǔ)言指南可以查看http://www.ognl.org/2.6.9/Documentation/html/LanguageGuide/index.html.
Result Types
到目前為止,我們只展示了配置Action的結(jié)果通過(guò)JSP渲染呈遞給用戶(hù).這是一個(gè)方案,但卻不是唯一的.事實(shí)上,Struts2支持多種類(lèi)型的結(jié)果.它們可以是可視的,或是可以與環(huán)境交互.
為action執(zhí)行的結(jié)果配置一個(gè)指定的類(lèi)型,將使用"type"屬性.如果沒(méi)有應(yīng)用這個(gè)屬性,默認(rèn)的"dispatcher"類(lèi)型將被使用-這將呈遞一個(gè)JSP的結(jié)果.如何配置action看起來(lái)像下面這樣:
1
<action name="my" class="com.fdar.infoq.MyAction" >
2
<result type="dispatcher">view.jsp</result>
3
</action>
Configuration
結(jié)果類(lèi)型配置在<package ... />標(biāo)簽內(nèi).這個(gè)配置與攔截器的配置相似.一個(gè)"name"屬性提供了一個(gè)結(jié)果類(lèi)型的唯一標(biāo)識(shí),并且"class"標(biāo)簽提供實(shí)現(xiàn)類(lèi).這還有第三個(gè)屬性"default"-這允許修改默認(rèn)的結(jié)果類(lèi)型.如果一個(gè)WEB應(yīng)用基于Velocity而不是JSP,修改默認(rèn)的將比輸入配置信息省時(shí).
1
<result-types>
2
<result-type name="dispatcher" default="true"
3
class="….dispatcher.ServletDispatcherResult"/>
4
<result-type name="redirect"
5
class="….dispatcher.ServletRedirectResult"/>
6
…
7
</result-types>
Implementing Result Types
與攔截器相似,它可能創(chuàng)建出你自己的結(jié)果類(lèi)型并將它們配置到你的WEB應(yīng)用中.一些常用的結(jié)果類(lèi)型已經(jīng)存在,所以,在你創(chuàng)建自己的之前,你應(yīng)該先看看你想創(chuàng)建的類(lèi)型是否已經(jīng)存在.
為了創(chuàng)建一個(gè)新的結(jié)果類(lèi)型,實(shí)現(xiàn)Result接口即可.
1
public interface Result extends Serializable
{
2
public void execute(ActionInvocation invocation) throws Exception;
3
}
ActionInvocation對(duì)象提供了對(duì)運(yùn)行環(huán)境的訪問(wèn),允許一個(gè)新的結(jié)果類(lèi)型訪問(wèn)來(lái)自剛剛運(yùn)行的action的信息,也可以訪問(wèn)執(zhí)行action的上下文.上下文包括了HttpServletRequest對(duì)象,可提供對(duì)當(dāng)前請(qǐng)求的輸出流的訪問(wèn).
上一章:Starting Struts2--Core Components(2)
下一章:Starting Struts2--Core Components(4)
PS:唉,進(jìn)度很慢,第三章還沒(méi)有結(jié)束,不過(guò)還好,還差一小部分就能結(jié)束了