攔截器與過濾器
攔截器是對調用的Action起作用,它提供了一種機制可以使開發者定義在一個action執行的前后執行的代碼,也可以在一個action執行前阻止其執行。同時也是提供了一種可以提取action中可重用的部分的方式,很多業務邏輯都是靠攔截實現的,比如校驗,驗證登錄權限(比如下載時跳轉到登陸頁面)等等。
過濾器是對整個的請求過程起作用!換句話說就是攔截器沒有過濾器的范圍廣。過濾器是在java web中,你傳入的request,response提前過濾掉一些信息,或者提前設置一些參數,然后再傳入servlet或者struts的 action進行業務邏輯,比如過濾掉非法url(不是login.do的地址請求,如果用戶沒有登陸都過濾掉),或者在傳入servlet或者 struts的action前統一設置字符集,或者去除掉一些非法字符(聊天室經常用到的,一些罵人的話,比如判斷用戶提交的數據是否存在非法字符等等。
Struts2攔截器是Struts2中的一個很重要的功能,本質是代理模式。本文將從概念開始,為大家講解Struts2攔截器的實現原理以及如何定義等等內容。
一、理解Struts2攔截器
1. Struts2攔截器是在訪問某個Action或Action的某個方法,字段之前或之后實施攔截,並且Struts2攔截器是可插拔的,攔截器是AOP的一種實現。
2. 攔截器棧(Interceptor Stack)。Struts2攔截器棧就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,Struts2攔截器鏈中的攔截器就會按其之前定義的順序被調用。
二、執行責任
這個執行職責有3種選擇:
1) 中止整個執行,直接返回一個字符串作為resultCode
2) 通過遞歸調用負責調用堆棧中下一個Interceptor的執行
3) 如果在堆棧內已經不存在任何的Interceptor,調用Action
三、實現Struts2攔截器原理
Struts2攔截器的實現原理相對簡單,當請求struts2的action時,Struts2會查找配置文件,並根據其配置實例化相對應的攔截器對象,然后串成一個列表,最后一個一個地調用列表中的攔截器。
四、定義Struts2攔截器
Struts2規定用戶自定義攔截器必須實現com.opensymphony.xwork2.interceptor.Interceptor接口。該接口聲明了3個方法
voidinit(); voiddestroy(); String intercept(ActionInvocation invocation)throws Exception;
不過,struts中又提供了幾個抽象類來簡化這一步驟。其中,init和destroy方法會在程序開始和結束時各執行一遍,不管使用了該攔截器與否,只要在struts.xml中聲明了該Struts2攔截器就會被執行。
intercept方法就是攔截的主體了,每次攔截器生效時都會執行其中的邏輯。
public abstract classAbstractInterceptorimplementsInterceptor; public abstract classMethodFilterInterceptorextendsAbstractInterceptor;
其中AbstractInterceptor提供了init()和destroy()的空實現,使用時只需要覆蓋intercept()方法;都是模板方法實現的;而MethodFilterInterceptor則提供了includeMethods和excludeMethods兩個屬性,用來過濾執行該過濾器的action的方法。可以通過param來加入或者排除需要過濾的方法。
一般來說,攔截器的寫法都差不多。看下面的示例:
package interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public classMyInterceptorimplementsInterceptor{ publicvoiddestroy(){ // TODO Auto-generated method stub } publicvoidinit(){ // TODO Auto-generated method stub } public String intercept(ActionInvocation invocation)throws Exception { System.out.println("Action執行前插入 代碼"); //執行目標方法 (調用下一個攔截器, 或執行Action) final String res = invocation.invoke(); System.out.println("Action執行后插入 代碼"); return res; }
Struts2攔截器需要在struts.xml中聲明,如下struts.xml配置文件,配置Struts2攔截器
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.objectFactory" value="spring" /> <package name="default" extends="struts-default"> <interceptors> <interceptor name="MyInterceptor" class="interceptor.MyInterceptor"></interceptor> <interceptor-stack name="myInterceptorStack"> <interceptor-ref name="MyInterceptor" /> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <action name="loginAction" class="loginAction"> <result name="fail">/index.jsp </result> <result name="success">/success.jsp</result> <interceptor-ref name="myInterceptorStack"></interceptor-ref> </action> </package> </struts>
攔截器全套簡單例子:

<body> <form action="loginAction" method="post" > 用戶名:<input type="text" name="user.name" /> 密碼: <input type="password" name="user.password" /> <input type="submit" value="登錄按鈕" > </form> </body>

<!-- Struts2核心過濾器 --> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" > <struts> <package name="struts2" extends="struts-default" namespace="/" > <!-- 攔截器 --> <interceptors> <!-- 配置自己的攔截器 --> <interceptor name="myTime" class="com.interceptor.TimeConsumingInterceptor"/> <interceptor name="myLogin" class="com.interceptor.CheckLoginInterceptor"/> <interceptor name="myother" class="com.interceptor.OtherInterceptor"></interceptor> <!-- 配置攔截器棧 --> <interceptor-stack name="myStack"> <!-- 默認自帶的攔截器,當配置自己的攔截器時不再走默認的攔截器,所以需要調用自帶的攔截器,並寫在第一行 --> <interceptor-ref name="defaultStack"/> <!-- 加入自己的攔截器 --> <interceptor-ref name="myTime"/> <interceptor-ref name="myLogin"/> </interceptor-stack> </interceptors> <!-- 定義默認的攔截器 每個Action都會自動引用,如果Action中引用了其它的攔截器 默認的攔截器將無效 --> <default-interceptor-ref name="myStack"/> <action name="*Action" class="com.struts.UsersAction" method="{1}" > <!-- 定義局部的攔截器: 當定義局部的攔截器,外面全局(默認)的攔截器則不會走,只會走局部的攔截器, 所以,我們在定義局部攔截器的同時,也要引用Struts2自帶的默認攔截器defaultStack。 不引用defaultStack至少會遭成取不到form表單提交的值。 <interceptor-ref name="defaultStack"/> <interceptor-ref name="myother"/> --> <!-- name屬性不寫默認success --> <result >/home.jsp</result> <result name="login">/file.jsp</result> <result name="input">/login.jsp</result> </action> </package> </struts>

package com.entity; /** * 用戶類 * @author asus * */ public class Users { /** 屬性 */ private String name; private String password; /** 構造方法 */ public Users() { super(); } public Users(String name, String password) { super(); this.name = name; this.password = password; } /** javaBean */ public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }

package com.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; /** * 攔截器類 * 作用:計算用戶從開始登錄到結束登錄消耗的毫秒數 * @author asus * */ public class TimeConsumingInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("執行順序:1進入TimeConsumingInterceptor"); //開始時間 long startTime = System.currentTimeMillis(); System.out.println("執行順序:2輸出開始時間startTime:"+startTime); //調用下一個攔截器,如果攔截器不存在,則執行Action。 String result = invocation.invoke(); System.out.println("執行順序:5輸出Action返回的結果:"+result); //結束時間 long endTimen = System.currentTimeMillis(); System.out.println("執行順序:6輸出結束時間endTimen:"+endTimen); //登陸使用時間 System.out.println("登陸使用時間:"+(endTimen-startTime)+"毫秒。。。"); return result; } }

package com.interceptor; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import com.entity.Users; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 攔截器類 * 作用:檢查用戶是否登陸,沒有登錄則不能向Action發送請求。 * * 測試請url訪問:loginUsers.action * @author asus * */ public class CheckLoginInterceptor implements Interceptor { @Override/** 銷毀的方法 */ public void destroy() { // TODO Auto-generated method stub } @Override/** 初始化方法 */ public void init() { // TODO Auto-generated method stub } @Override/** 攔截器 */ public String intercept(ActionInvocation invocation) throws Exception { System.out.println("執行順序:3進入CheckLoginInterceptor"); //得到request對象 HttpServletRequest request = ServletActionContext.getRequest(); //取得登陸頁面用戶輸入的賬號密碼若不為空的話讓其通過 String name = request.getParameter("user.name"); String password = request.getParameter("user.password"); //取session中保存的用戶登錄信息 Users users = (Users) invocation.getInvocationContext().getSession().get("users"); if(name!=null && password!=null){ //若是登陸頁面請求Action,則通過 return invocation.invoke(); }else if(users==null){ return Action.INPUT;//input常量 } //若已經登陸,則讓其通過訪問下一個攔截器,或Action。 return invocation.invoke(); } }

package com.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; /** * 攔截器 * 作用:測試定義局部攔截器執行順序。 * @author asus * */ public class OtherInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("局部攔截器"); return invocation.invoke(); } }

package com.struts; import com.entity.Users; import com.opensymphony.xwork2.ActionSupport; /** * 控制器類 * 作用:處理用戶的請求 * @author asus * */ public class UsersAction extends ActionSupport { /** 屬性 */ private Users user; /** 重寫execute方法 :此方法作用,為指定處理請求的方法時,默認走此方法*/ public String execute(){ return ""; } /** 登陸驗證的方法 */ public String login(){ System.out.println("執行順序:4進入login()"); if(user!=null){ if(user.getName().equals("admin") && user.getPassword().equals("admin")){ return SUCCESS; } } return LOGIN; } /** JavaBean */ public Users getUser() { return user; } public void setUser(Users user) { this.user = user; } }

<body> 登錄成功進入首頁。。 </body>

<body> 登陸失敗頁面。。 </body>
全局攔截器:控制台輸出登錄成功的攔截器執行順序。
局部攔截器:控制台輸出登錄成功的攔截器執行順序。當定義局部攔截器時,則不會再走全局攔截器。