struts2學習之---攔截器機制
攔截器體系是struts2框架的重要組成部分,可以把struts2理解成一個空容器,而大量的內建攔截器完成了該框架的大部分操作。比如:param攔截器負責解析HTTP請求的引數,並設定Action的屬性;servlet-config攔截器直接將HTTP請求中的HttpServletRequest例項和HttpServletResponse例項傳給Action;fileUpload攔截器則負責解析請求引數中的檔案域,並將一個檔案域設定成Action的三個屬性。
struts2攔截器是可插拔式的攔截器。
1.攔截器在struts2中的作用
struts2框架的絕大部分功能都是通過攔截器來完成的。當StrutsPrepareAndExecuteFilter攔截到使用者請求之後,大量攔截器將會對使用者請求進行處理,然後才會呼叫使用者開發的Action例項的方法來處理使用者請求。
攔截器與Action的關係:StrutsPrepareAndExecuteFilter初始化一個AcitonProxy例項,並呼叫它的execute()方法,攔截器方法會先攔截,並處理使用者請求,然後才到Action的execute方法處理使用者請求,返回一個邏輯檢視名,系統負責將邏輯檢視對應的資源呈現給使用者。
struts2已經預設啟用了大量通用功能的攔截器,只要配置Action時所在的package繼承了struts-default包,這些攔截器就會起作用。
2.配置攔截器
在struts.xml檔案中定義攔截器只需為攔截器類指定一個攔截器名。
<interceptor name="攔截器名" class="攔截器實現類"/>
如果需要在配置攔截器時傳入攔截器引數,則需要在<interceptor.../>
元素中使用<param.../>
子元素。
<package name="">
<interceptors>
<interceptor name="攔截器名" class="攔截器實現類">
<param name="引數名" >引數值</param>
</interceptor>
<interceprors>
</package>
把多個攔截器連在一起組成攔截器棧,如果需要在Action執行前同時做登入檢查,安全檢查和記錄日誌,則可以把這三個動作對應的攔截器組成一個攔截器棧。
<interceptor-stack name="攔截器棧名">
<interceptor-ref name="攔截器一"/>
<interceptor-ref name="攔截器二"/>
...
</interceptor-stack>
因為攔截器棧與攔截器的功能幾乎完全相同,因此可能出現的是攔截器棧裡也可包含攔截器棧。
系統為攔截器指定引數有如下兩個時機:
- 定義攔截器時指定引數值:這種引數值將作為攔截器引數的預設引數值。通過
<interceptor>
元素來定義攔截器。 - 使用攔截器時指定引數值:在配置Action時攔截器引數指定值。通過
<interceptor-ref>
元素來使用攔截器。
使用攔截器時指定的引數值將會覆蓋預設的引數值。
3.使用攔截器的配置語法
一旦定義了攔截器和攔截器棧後,就可以使用這個攔截器或攔截器棧來攔截Action了,攔截器(包含攔截器棧)的攔截行為將會在Action的execute方法執行之前被執行。
通過<interceptor-ref.../>
元素可以在Action內使用攔截器。
<action name="" calss="">
<interceptor-ref name="攔截器名或攔截器棧名"/>
<interceptor-ref name="攔截器名或攔截器棧名"/>
</action>
4.配置預設攔截器
當配置一個包時,可以為其指定預設攔截器。一旦為某個包指定了預設的攔截器,如果該包中的Action沒有顯示指定攔截器,則預設的攔截器將會起作用。一旦為該包中的Action顯示應用了某個攔截器,則預設的攔截器不會起作用,如果該Action還需要使用該預設攔截器,則必須手動配置該攔截器的引用。
<package name="">
<default-inerceptor-ref name="已經存在的攔截器或攔截器棧名"/>
</package>
每個<package...>
元素只能有一個<default-interceptor-ref.../>
子元素,即每個包只能指定一個預設攔截器。
配置預設攔截器是一種使用攔截器的方式—避免在每個Action中單獨配置攔截器,通過在該包下配置攔截器,可以實現為該包下所有Action同時配置相同的攔截器。
struts-default.xml
<struts>
<package name="struts-default">
<default-interceptor-ref name="defaultStack"/>
</package>
</struts>
攔截器相關配置如下:
<interceptors.../>
元素:該元素用於定義攔截器。該元素包含<interceptor.../>
和<interceptor-stack.../>
子元素用於定義攔截器和攔截器棧。<interceptor.../>
:該元素用於定義單個攔截器,指定name和class屬性,分別指定攔截器的名字和實現類。<interceptor-stack.../>
:該元素用於定義攔截器棧。包含多個<interceptor-ref.../>
元素,用於將多個攔截器或攔截器棧組合成一個新的攔截器棧。<interceptor-ref.../>
:該元素引用一個攔截器或攔截器棧,表明應用指定攔截器。name屬性,該屬性值為一個已經定義的攔截器或攔截器棧。該元素可以作為<interceptor-stack.../>
和<action.../>
元素的子元素使用。<param.../>
:該元素用於為攔截器指定引數,可以作為<interceptor.../>
和<interceptor-ref.../>
元素的子元素使用。<default-interceptor-ref.../>
:該元素為指定包配置預設攔截器。該元素作為<package.../>
元素的子元素使用。
5.實現攔截器類
如果使用者要開發自己的攔截器類,應該實現com.opensymphony.xwork2.interceptor.Interceptor介面。
public interface Interceptor extends Serializable{
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
- init():在該攔截器被例項化之後,在該攔截器執行攔截之前,系統將回調該方法。該方法的方法體主要用於初始化資源。
- destroy():在攔截器例項被銷燬之前,系統將回調該攔截器的destory方法,該方法用於銷燬在init()方法裡開啟的資源。
- intercept():該方法是使用者需要實現的攔截動作。該方法的ActionInvocation引數包含了被攔截的Action的引用,可以通過呼叫該引數的invoke方法,將控制權轉給下一個攔截器,或者轉給Action的execute方法。
struts2還提供了一個AbstractInterceptor類,該類提供了一個init()和destory()方法的空實現,如果實現的攔截器不需要開啟資源,則無須實現這兩個方法。
6.攔截方法的攔截器
在某些情況下,如果不想攔截所有的方法,只需要攔截指定方法,此時就需要使用struts2攔截器的發放過濾特性。
為了實現方法過濾特性,struts2提供了一個MethodFilterInterceptor類,該類是AbstractInterceptor類的子類。MethodFilterInterceptor類重寫了AbstractInterceptor類的intercept()方法,但提供了一個doIntercept()抽象方法。
實際上方法過濾的攔截器與實現普通攔截器並沒有太大的區別,只需要注意的是:實現方法過濾的攔截器需要繼承MethodFilterInterceptor抽象類,並且重寫doIntercept()方法定義對Action的攔截邏輯。
在MethodFilterInterceptor方法中,額外增加如下兩個方法:
- public void setExcludeMethods(String execludeMethods):排除需要過濾的方法—設定方法“黑名單”,所有在excludeMethods字串中列出的方法都不會被攔截。
- public void setIncludeMethods(String includeMethods):設定需要過濾的方法—設定方法“白名單”,所有在includeMethods字串中列出的方法都會被攔截。
如果一個方法同時在excludeMethods和includeMethods中列出,則該方法會被攔截。
<action name="" class="">
<interceptor-ref name="">
<param name="excludeMethods">execute</param>
</interceptor>
</action>
struts2中提供了這種方法過濾的攔截器有如下幾個:
- TokenInterceptor
- TokenSessionStoreInterceptor
- DefaultWorkflowInterceptor
- ValidationInterceptor
7.攔截器的執行順序
在Action的控制方法執行之前,位於攔截器鏈前面的攔截器將先發生作用;在Action的控制方法執行之後,位於攔截器鏈前面的攔截器將後發生作用。
8.攔截結果的監聽器
為了精確定義在execute()方法執行結束後,在處理物理資源轉向之前的動作,struts2提供了用於攔截結果的監聽器,這個監聽器是用過手動註冊在攔截器內部的。
PreResultListener,把該監聽器註冊在攔截器中,這樣可以只要該攔截器起作用的地方,這個攔截介面的監聽器都會被觸發。
public class MyPreResultListener implements PreResultListener{
public void beforeResult(ActionInvocation invocation,String resultCode){
}
}
public class BeforeResultInterceptor extends AbstractInterceptor{
public String intercept(ActionInvocation invocation){
invocation.addPreResultListener(new MyPreResultListener());
}
}
resultCode,這個引數就是被攔截的Action的被攔截方法的返回值。
不要在PreResultListener監聽器的beforeResult方法中通過ActionInvocation引數呼叫invoke()方法。
9.覆蓋攔截器棧裡特定攔截器的引數
有時候,Action需要使用一個攔截器棧,當使用這個攔截器棧時,又需要覆蓋該攔截器棧中某個攔截器的指定引數值。此時需要在配置使用攔截器棧的<interceptor-ref.../>
元素中使用<param.../>
元素來傳入引數,在<param.../>
元素中指定引數名時應使用<攔截器名>.<引數名>這種方式。
<package name="">
<interceptors>
<interceptor-ref name="first">
<param name="name">第一個</param>
</interceptor-ref>
<interceptors>
<action name="" class="">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="first">
<param name="first.name">改名後的攔截器</param>
</interceptor-ref>
</action>
</package>