Struts 2之攔截器
攔截器概述
Struts2攔截器是在訪問某個Action或Action的某個方法,字段之前或之后實施攔截,并且Struts2攔截器是可插拔的,攔截器是AOP(Aspect Oriented Programming,面向切面編程)的一種實現
攔截器棧(InterceptorStack)。Struts2攔截器棧就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,Struts2攔截器鏈中的攔截器就會按其之前定義的順序被調用。
Struts2規定用戶自定義攔截器必須實現com.opensymphony.xwork2.interceptor.Interceptor接口,該接口聲明了3個方法
void init(); void destroy(); Stringintercept(ActionInvocation invocation) throws Exception;其中,init和destroy方法會在程序開始和結束時各執行一遍,不管使用了該攔截器與否,只要在struts.xml中聲明了該Struts2攔截器就會被執行。
intercept方法就是攔截的主體了,每次攔截器生效時都會執行其中的邏輯
不過,struts中又提供了幾個抽象類來簡化這一步驟
public abstrac tclass AbstractInterceptor implements Interceptor;
public abstract class MethodFilterInterceptor extends AbstractInterceptor;
都是模板方法實現的。
其中AbstractInterceptor提供了init()和destroy()的空實現,使用時只需要覆蓋intercept()方法;
而MethodFilterInterceptor則提供了includeMethods和excludeMethods兩個屬性,用來過濾執行該過濾器的action的方法。可以通過param來加入或者排除需要過濾的方法。
實例
直接實現Interceptor接口
public class MyInterceptor implements Interceptor { public void destroy() { // TODO Auto-generated method stub } public void init() { // TODO Auto-generated method stub } public String intercept(ActionInvocationinvocation) throws Exception { System.out.println("Action執行前插入 代碼"); //執行目標方法 (調用下一個攔截器, 或執行Action) final String res =invocation.invoke(); System.out.println("Action執行后插入 代碼"); return res; } }
配置
<package name="default" extends="struts-default"> <interceptors> <interceptor name="MyInterceptor" class="interceptor.MyInterceptor"></interceptor> <interceptor-stack name="myInterceptorStack"> <interceptor-refname="MyInterceptor"/> <interceptor-refname="defaultStack"/> </interceptor-stack> </interceptors> <action name="loginAction" class="loginAction"> <result name="fail">/index.jsp</result> <result name="success">/success.jsp</result> <interceptor-refname="myInterceptorStack"></interceptor-ref> </action> </package>
特別注意,在使用攔截器的時候,最后一定要引用struts2自帶的攔截器缺省堆棧defaultStack,否則出錯
繼承AbstractInterceptor抽象類
我們嘗試編寫一個Session過濾用的攔截器,該攔截器查看用戶Session中是否存在特定的屬性(LOGIN屬性)如果不存在,中止后續操作定位到LOGIN,否則執行原定操作,代碼為:
public class CheckLoginInterceptor extends AbstractInterceptor { public static final String LOGIN_KEY = "LOGIN"; public static final String LOGIN_PAGE = "global.login"; public String intercept(ActionInvocationactionInvocation) throws Exception { System.out.println("begin checklogin interceptor!"); // 對LoginAction不做該項攔截 Object action =actionInvocation.getAction(); if (action instanceof LoginAction) { System.out.println("exit checklogin, because this is login action."); return actionInvocation.invoke(); } // 確認Session中是否存在LOGIN Map session =actionInvocation.getInvocationContext().getSession(); String login = (String)session.get(LOGIN_KEY); if (login != null &&login.length() > 0) { // 存在的情況下進行后續操作。 System.out.println("alreadylogin!"); return actionInvocation.invoke(); } else { // 否則終止后續操作,返回LOGIN System.out.println("no login,forward login page!"); return LOGIN_PAGE; } } }
注冊攔截器
<interceptors> <interceptor name="login" class="com.clf.CheckLoginInterceptor"/> <interceptor-stack name="teamwareStack"> <interceptor-ref name="login"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors>
將上述攔截器設定為默認攔截器:
<default-interceptor-ref name="teamwareStack"/>
這樣在后續同一個package內部的所有Action執行之前都會被login攔截。
繼承MethodFilterInterceptor抽象類
MethodFilterInterceptor類重寫了AbstractInterceptor類的intercept(ActionInvocationinvocation)方法,但提供了一個doIntercept(ActionInvocation invocation)抽象方法。從這種設計方式可以看出,MethodFilterInterceptor類的intercept已經實現了對Action的攔截行為(只是實現了方法過濾的邏輯),但真正的攔截邏輯還需要開發者提供,也就是通過回調doIntercept方法實現。可見,如果用戶需要實現自己的攔截邏輯,則應該重寫doIntercept(ActionInvocation invocation)方法。
下面是一個簡單的方法過濾的示例應用,方法過濾的攔截器代碼如下。
//攔截方法的攔截器,應該繼承MethodFilterInterceptor抽象類 public class MyFilterInterceptor extends MethodFilterInterceptor{ //簡單攔截器的名字 private String name; //為該簡單攔截器設置名字的setter方法 public void setName(String name){ this.name= name; } //重寫doIntercept方法,實現對Action的攔截邏輯 public String doIntercept(ActionInvocation invocation) throws Exception{ //取得被攔截的Action實例 LoginAction action = (LoginAction)invocation.getAction(); //打印執行開始的時間 System.out.println(name + " 攔截器的動作---------" + "開始執行登錄Action的時間為:" + newDate()); //取得開始執行Action的時間 long start = System.currentTimeMillis(); //執行該攔截器的后一個攔截器,或者直接指定Action的execute方法 String result = invocation.invoke(); //打印執行結束的時間 System.out.println(name + " 攔截器的動作---------" + "執行完登錄Action的時間為:" + newDate()); long end = System.currentTimeMillis(); //打印執行該Action所花費的時間 System.out.println(name + " 攔截器的動作---------" + "執行完該Action的事件為" + (end- start) + "毫秒"); return result; } }
從上面的代碼中可以看出,上面攔截器的攔截邏輯與前面簡單攔截器的攔截邏輯相似,只是之前是需要重寫intercept方法,現在是重寫doIntercept方法。
實際上,實現方法過濾的攔截器與實現普通攔截器并沒有太大的區別,只需要注意兩個地方:實現方法過濾的攔截器需要繼承MethodFilterInterceptor抽象類,并且重寫doIntercept方法定義對Action的攔截邏輯。
在MethodFilterInterceptor方法中,額外增加了如下兩個方法:
public void setExcludeMethods(String excludeMethods):排除需要過濾的方法——設置方法"黑名單",所有在excludeMethods字符串中列出的方法都不會被攔截。
public void setIncludeMethods(String includeMethods):設置需要過濾的方法——設置方法"白名單",所有在includeMethods字符串中列出的方法都會被攔截。
注意:如果一個方法同時在excludeMethods和includeMethods中列出,則該方法會被攔截。
因為MethodFilterInterceptor類包含了如上的兩個方法,則該攔截器的子類也會獲得這兩個方法。可以在配置文件中指定需要被攔截,或者不需要被攔截的方法。
方法過濾示例應用的配置片段如下:
<!-- 配置本系統所使用的包--> <package name="clf" extends="struts-default"> <!-- 應用所需使用的攔截器都在該元素下配置 --> <interceptors> <!-- 配置mysimple攔截器 --> <interceptor name="mysimple" class="com.clf.MyFilterInterceptor"> <!-- 為攔截器指定參數值 --> <param name="name">攔截方法的攔截器</param> </interceptor> </interceptors> <action name="loginPro" class="com.clf.LoginAction"> <result name="error">/WEB-INF/content/error.jsp</result> <result name="success">/WEB-INF/content/welcome.jsp</result> <!-- 配置系統的默認攔截器 --> <interceptor-ref name="defaultStack"/> <!-- 應用自定義的mysimple攔截器 --> <interceptor-ref name="mysimple"> <!-- 重新指定name屬性的屬性值 --> <param name="name">改名后的攔截方法過濾攔截器</param> <!-- 指定execute方法不需要被攔截--> <param name="excludeMethods">execute</param> </interceptor-ref> </action> <action name="*"> <result>/WEB-INF/content/{1}.jsp</result> </action> </package>
上面配置文件的代碼通過excludeMethods屬性指定了execute方法無須被攔截,如果瀏覽者在瀏覽器中再次向login的Action發送請求,在Tomcat控制臺將看不到任何輸出,表明該攔截器沒有攔截Action的execute方法。
如果需要同時指定多個方法不被該攔截器攔截,則多個方法之間以英文逗號(,)隔開。看如下的配置片段:
Struts 2中提供了這種方法過濾的攔截器有如下幾個:
TokenInterceptor
TokenSessionStoreInterceptor
DefaultWorkflowInterceptor
ValidationInterceptor
Struts2自帶攔截器
AliasInterceptor alias
在不同請求之間將請求參數在不同名字件轉換,請求內容不變
ChainingInterceptor chain
讓前一個Action的屬性可以被后一個Action訪問,現在和chain類型的result(<resulttype="chain">)結合使用。
CheckboxInterceptor checkbox
添加了checkbox自動處理代碼,將沒有選中的checkbox的內容設定為false,而html默認情況下不提交沒有選中的checkbox。
CookiesInterceptor cookies
使用配置的name,value來是指cookies
Conversion ErrorInterceptor conversionError
將錯誤從ActionContext中添加到Action的屬性字段中。
Create SessionInterceptor createSession
自動的創建HttpSession,用來為需要使用到HttpSession的攔截器服務。
DebuggingInterceptor debugging
提供不同的調試用的頁面來展現內部的數據狀況。
Execute and WaitInterceptor execAndWait
在后臺執行Action,同時將用戶帶到一個中間的等待頁面。
ExceptionInterceptor exception
將異常定位到一個畫面
File UploadInterceptor fileUpload
提供文件上傳功能
I18n Interceptor i18n
記錄用戶選擇的locale
LoggerInterceptor logger
輸出Action的名字
Message StoreInterceptor store
存儲或者訪問實現ValidationAware接口的Action類出現的消息,錯誤,字段錯誤等。
Model DrivenInterceptor model-driven
如果一個類實現了ModelDriven,將getModel得到的結果放在ValueStack中。
Scoped ModelDriven scoped-model-driven
如果一個Action實現了ScopedModelDriven,則這個攔截器會從相應的Scope中取出model調用Action的setModel方法將其放入Action內部。
ParametersInterceptor params
將請求中的參數設置到Action中去。
PrepareInterceptor prepare
如果Acton實現了Preparable,則該攔截器調用Action類的prepare方法。
ScopeInterceptor scope
將Action狀態存入session和application的簡單方法。
Servlet ConfigInterceptor servletConfig
提供訪問HttpServletRequest和HttpServletResponse的方法,以Map的方式訪問。
StaticParameters Interceptor staticParams
從struts.xml文件中將<action>中的<param>中的內容設置到對應的Action中。
RolesInterceptor roles
確定用戶是否具有JAAS指定的Role,否則不予執行。
TimerInterceptor timer
輸出Action執行的時間
TokenInterceptor token
通過Token來避免雙擊
Token SessionInterceptor tokenSession
和Token Interceptor一樣,不過雙擊的時候把請求的數據存儲在Session中
ValidationInterceptor validation
使用action-validation.xml文件中定義的內容校驗提交的數據。
WorkflowInterceptor workflow
調用Action的validate方法,一旦有錯誤返回,重新定位到INPUT畫面
Parameter FilterInterceptor N/A
從參數列表中刪除不必要的參數
ProfilingInterceptor profiling
通過參數激活profile
注冊并引用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"> <interceptor-ref name="myStack"/> <result name="input">login.jsp</result> <result name="success" type="redirect-action">/secure/home</result> </action> </package>
上述說明的攔截器在默認的Struts2應用中,根據慣例配置了若干個攔截器堆棧,詳細情參看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>每一個攔截器都可以配置參數,有兩種方式配置參數,一是針對每一個攔截器定義參數,二是針對一個攔截器堆棧統一定義所有的參數,例如:
<interceptor-ref name="validation"> <param name="excludeMethods">myValidationExcudeMethod</param> </interceptor-ref> <interceptor-refname="workflow"> <param name="excludeMethods">myWorkflowExcludeMethod</param> </interceptor-ref>或者
<interceptor-ref name="defaultStack"> <param name="validation.excludeMethods">myValidationExcludeMethod</param> <param name="workflow.excludeMethods">myWorkflowExcludeMethod</param> </interceptor-ref>
每一個攔截器都有兩個默認的參數:
excludeMethods - 過濾掉不使用攔截器的方法和
includeMethods – 使用攔截器的方法。
攔截器執行的順序按照定義的順序執行