看到網上很多介紹關於攔截器的文章,感覺都不錯,但是都沒有很詳細全面的介紹,所以我就博衆家之長,呵呵,寫了篇關於struts攔截器的東西。
攔截器的工作原理如上圖,在工作的時候每一個Action請求都被包裝在一堆攔截器的內部。攔截器可以在Action執行直線做相似的操作也可以在Action執行直後做回收操作。
每一個Action既可以將操作轉交給下面的攔截器,Action也可以直接退出操作返回客戶既定的畫面。
下面我們來講講如何使用struts2攔截器,或者是自定義攔截器。
先說說使用Struts自帶的攔截器:
在Struts2中已經在struts-default.xml中預定義了一些自帶的攔截器,如timer、params等。如果在<package>標籤中繼承struts-default,則當前package就會自動擁有struts-default.xml中的所有配置。代碼如下:
<package name="demo" extends="struts-default" > ... </package>
在struts-default.xml中有一個默認的引用,在默認情況下(也就是<action>中未引用攔截器時)會自動引用一些攔截器。這個默認的攔截器引用如下:
<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>
上面在defaultStack中引用的攔截器都可以在<action>中不經過引用就可以使用(如果在<action>中引用了任何攔截器後,要使用在defaultStack中定義的攔截器,也需要在<action>中重新引用,在後面的“二、通過請求調用Action的setter方法(params)”將詳細講解)。
下面我們來看幾個簡單的攔截器的使用方法。
一、記錄攔截器和execute方法的執行時間(timer攔截器)
timer是Struts2中最簡單的攔截器,這個攔截器對應的類是com.opensymphony.xwork2.interceptor.TimerInterceptor。它的功能是記錄execute方法和其他攔截器(在timer後面定義的攔截器)的intercept方法執行的時間總和。如下面的配置代碼所示:
<action name="first" class="action.FirstAction"> <interceptor-ref name="logger"/> <interceptor-ref name="timer" /> </action>
由於在timer後面沒有其他的攔截器定義,因此,timer只能記錄execute方法的執行時間,在訪問first動作時,會在控制檯輸出類似下面的一條信息:
信息: Executed action [/test/first!execute] took 16 ms.
在使用timer攔截器時,需要commons-logging.jar的支持。將logger引用放到timer的後面,就可以記錄logger攔截器的intercept方法和Action的execute方法的執行時間總和,代碼如下:
<action name="first" class="action.FirstAction"> <interceptor-ref name="timer" /> <interceptor-ref name="logger"/> </action>
大家可以使用如下的Action類來測試一下timer攔截器:
package action;
import com.opensymphony.xwork2.ActionSupport;
public class FirstAction extends ActionSupport
{
public String execute() throws Exception
{
Thread.sleep(2000); // 延遲2秒
return null;
}
}
如果只記錄execute方法的執行時間,一般會輸出如下的信息:
信息: Executed action [/test/first!execute] took 2000 ms.
二、通過請求調用Action的setter方法(params)
當客戶端的一個form向服務端提交請求時,如有一個textfield,代碼如下:
<s:form action="first" namespace="/test"> <s:textfield name="name"/> <s:submit/> </s:form>
在提交後,Struts2將會自動調用first動作類中的setName方法,並將name文本框中的值通過setName方法的參數傳入。實際上,這個操作是由params攔截器完成的,params對應的類是com.opensymphony.xwork2.interceptor.ParametersInterceptor。由於params已經在defaultStack中定義,因此,在未引用攔截器的<action>中是會自動引用params的,如下面的配置代碼,在訪問first動作時,Struts2是會自動執行相應的setter方法的。
<action name="first" class="action.FirstAction">
... ...
</action>
但如果在<action>中引用了其他的攔截器,就必須再次引用params攔截器,Struts2才能調用相應的setter方法。如下面的配置代碼所示:
<action name="first" class="action.FirstAction"> <interceptor-ref name="timer" /> <interceptor-ref name="params"/> </action>
三、通過配置參數調用Action的setter方法(static-params)
static-params攔截器可以通過配置<params>標籤來調用Action類的相應的setter方法,static-params攔截器對應的類是com.opensymphony.xwork2.interceptor.StaticParametersInterceptor。
下面配置代碼演示瞭如何使用static-params攔截器:
<action name="first" class="action.FirstAction"> <interceptor-ref name="timer" /> <param name="who">比爾</param> <interceptor-ref name="params"/> <interceptor-ref name="static-params"/> </action>
如果first動作使用上面的配置,在訪問first動作時,Struts2會自動調用setWho方法將“比爾”作爲參數值傳入setWho方法。
四、使用攔截器棧
爲了能在多個動作中方便地引用同一個或幾個攔截器,可以使用攔截器棧將這些攔截器作爲一個整體來引用。攔截器棧要在<package>標籤中使用<interceptors>和子標籤<interceptor-stack>來定義。代碼如下:
<package name="demo" extends="struts-default" > <interceptors> <interceptor-stack name="mystack"> <interceptor-ref name="timer" /> <interceptor-ref name="logger" /> <interceptor-ref name="params" /> <interceptor-ref name="static-params" /> </interceptor-stack> </interceptors> <action name="first" class="action.FirstAction"> <param name="who">比爾</param> <interceptor-ref name="mystack"/> </action> </package>
這樣以來,我們就可以象使用攔截器一樣使用攔截器棧。
如何自定義一個攔截器?
自定義一個攔截器,我們需要三步就可以完成了:
1 自定義一個實現Interceptor接口(或者繼承自AbstractInterceptor)的類。
2 在strutx.xml中註冊上一步中定義的攔截器。
3 在需要使用的Action中引用上述定義的攔截器,爲了方便也可將攔截器定義爲默認的攔截器,這樣在不加特殊聲明的情況下所有的Action都被這個攔截器攔截。
Interceptor接口聲明瞭三個方法:
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
Init方法在攔截器類被創建之後,在對Action鏡像攔截之前調用,相當於一個post-constructor方法,使用這個方法可以給攔截器類做必要的初始話操作。
Destroy方法在攔截器被垃圾回收之前調用,用來回收init方法初始化的資源。
Intercept是攔截器的主要攔截方法,如果需要調用後續的Action或者攔截器,只需要在該方法中調用invocation.invoke()方法即可,在該方法調用的前後可以插入Action調用前後攔截器需要做的方法。如果不需要調用後續的方法,則返回一個String類型的對象即可,例如Action.SUCCESS。
另外AbstractInterceptor提供了一個簡單的Interceptor的實現,這個實現爲:
public abstract class AbstractInterceptor implements Interceptor {
public void init() {
}
public void destroy() {
}
public abstract String intercept(ActionInvocation invocation) throws Exception;
}
在不需要編寫init和destroy方法的時候,只需要從AbstractInterceptor繼承而來,實現intercept方法即可。
我們嘗試編寫一個Session過濾用的攔截器,該攔截器查看用戶Session中是否存在特定的屬性(LOGIN屬性)如果不存在,中止後續操作定位到LOGIN,否則執行原定操作,代碼爲:
<interceptors> <interceptor name="login" class="com.jpleasure.teamware.util.CheckLoginInterceptor"/> <interceptor-stack name="teamwareStack"> <interceptor-ref name="login"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors>
將上述攔截器設定爲默認攔截器:
<default-interceptor-ref name="teamwareStack"/>
這樣在後續同一個package內部的所有Action執行之前都會被login攔截。
註冊並引用Interceptor
<package name="default" extends="struts-default"> <interceptors> <interceptor name="timer" class=".."/> <interceptor name="logger" class=".."/> </interceptors> <action name="login" class="tutorial.Login"> <interceptor-ref name="timer"/> <interceptor-ref name="logger"/> <result name="input">login.jsp</result> <result name="success" type="redirect-action">/secure/home</result> </action> </package>
可以將多個攔截器合併在一起作爲一個堆棧調用,當一個攔截器堆棧被附加到一個Action的時候,要想Action執行,必須執行攔截器堆棧中的每一個攔截器。
- <package name="default" extends="struts-default">
- <interceptors>
- <interceptor name="timer" class=".."/>
- <interceptor name="logger" class=".."/>
- <interceptor-stack name="myStack">
- <interceptor-ref name="timer"/>
- <interceptor-ref name="logger"/>
- </interceptor-stack>
- </interceptors>
- <action name="login" class="tutuorial.Login">