1、攔截器小介
攔截器的功能類似于web.xml文件中的Filter,能對用戶的請求進行攔截,通過攔截用戶的請求來實現對頁面的控制。攔截器是在Struts-core-2.2.3.jar中進行配置的,原始的攔截器是在struts-default.xml中配置的,里面封存了攔截器的基本使用方法。
Struts2攔截器功能類似于Servlet過濾器。在Action執行execute方法前,Struts2會首先執行struts.xml中引用的攔截器,如果有多個攔截器則會按照上下順序依次執行,在執行完所有的攔截器的interceptor方法后,會執行Action的execute方法。
Struts2的攔截器必須從com.opensymphoy.xwork2.interceptor.Interceptor中實現該接口,在被定義的攔截器中有下面三個方法需要被實現:
1
2
3
|
void destroy(); void init(); String intercept(ActionInvocation invocation) throws Exception; |
自定義的攔截器需要重寫上面三個方法。另外Struts2的攔截器配置文件struts.xml它是繼承了原始文件struts-default.xml文件的,這樣在相應的<package>中就會自動擁有struts-default.xml中的所有配置信息了。具體代碼如下:
1
|
< package name = "demo" extends = "struts-default" > ... </ package > |
2、添加攔截器
想要使用攔截器必須要經過配置,struts2采用的是映射的方法,所以想用使用某一個功能就必須在配置文件中配置,攔截器也不例外。所以必須在package中添加相應的攔截器元素,同時將攔截器關聯相應的class文件,這樣在執行action前才會執行相應的攔截器,具體使用方法如下。
(1)添加配置文件struts.xml,并在該文件中添加攔截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
< package name = "testLogin" namespace = "/" extends = "struts-default" > <!-- 攔截器 --> < interceptors > < interceptor name = "myInterceptor" class = "com.interceptor.MyInterceptor" ></ interceptor > </ interceptors > < action name = "demo" class = "com.action.LoginAction" > < result name = "error" type = "redirect" >/error.jsp</ result > < result name = "success" >/success.jsp</ result > < result name = "checkError" >/checkSession.jsp</ result > < interceptor-ref name = "myInterceptor" ></ interceptor-ref > < interceptor-ref name = "defaultStack" ></ interceptor-ref > </ action > </ package > |
上面的package中添加了一個名為myInterceptor的攔截器,并為該攔截器注冊了一個java類,該類名稱為MyInterceptor,并被封存在com.interceptor包中。另外還在該package中添加了相應的action,在執行該action前會首先執行myInterceptor攔截器。
(2)編寫被注冊的攔截器類MyInterceptor,該類必須實現com.opensymphoy.xwork2.interceptor.Interceptor接口,并重寫相應的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package com.interceptor; import java.util.Map; import com.entity.User; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class MyInterceptor implements Interceptor{ private User user; public User getUser() { return user; } public void setUser(User user) { this .user = user; } @Override public void destroy() { // TODO Auto-generated method stub System.out.println( "----destroy()----" ); } @Override public void init() { // TODO Auto-generated method stub System.out.println( "-----Init()-------" ); } @Override public String intercept(ActionInvocation invocation) throws Exception { // TODO Auto-generated method stub System.out.println( "----intercept()------" ); Map<String, Object> session= invocation.getInvocationContext().getSession(); if (session.get( "username" )!= null ){ System.out.println( "登陸成功!" ); //session.put("username",user.getUsername()); return invocation.invoke(); } else { System.out.println( "登陸失敗!" ); return "checkError" ; } } } |
(3)經過前面兩步后,攔截器已經配置完成,最后一部就是使用攔截器了,在顯示頁面上添加相應的標簽,并為標簽指定上面創建的名為demo的action,然后執行頁面即可在控制臺中打印出相應的攔截器內容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> < html > < head > < meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" > < title >Insert title here</ title > </ head > < body > < form action = "demo" > 用戶名:< input type = "text" name = "username" >< br > 密 碼:< input type = "text" name = "password" >< br > < input type = "submit" name = "ok" value = "提交" > </ form > </ body > </ html > |
打印輸出內容:
分析輸出結果,程序編譯階段首先會去讀取配置文件struts.xml,在該配置文件action中順序查找是否添加了攔截器,如果添加了攔截器則根據攔截器名稱在<interceptors>中查找是否定義了該攔截器或者攔截器棧,如果發現定義的是攔截器則根據攔截器查找對應的注冊的class,最后在包內查找注冊的class并執行相應的init()方法;程序運行階段的大致流程和編譯階段類似,用戶在前臺提交請求后,會按照注冊的action在struts.xml中查找與之相對應的,如果查找到將會查找攔截器,沒有查找到的話會相應的拋錯,最后執行攔截器注冊類的intercept方法。
3、攔截器棧
攔截器同樣有棧的概念,它是將使用的攔截器定義到共有的狀態下來實現統一管理,這樣在package中就可以做到攔截器的共享了,大大便利了攔截器的使用。在一個package中往往會用到重復的interceptor,如果每次都在Action中添加interceptor-ref的話就會很麻煩,那么攔截器棧就是為了解決這個問題而產生的,具體配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
< package name = "testLogin" namespace = "/" extends = "struts-default" > <!-- 攔截器 --> < interceptors > < interceptor name = "myInterceptor" class = "com.interceptor.MyInterceptor" ></ interceptor > <!-- 定義公共的攔截器鏈,在action標簽中只需要引用攔截器鏈 --> < interceptor-stack name = "defaultstack1" > < interceptor-ref name = "myInterceptor" ></ interceptor-ref > < interceptor-ref name = "defaultStack" ></ interceptor-ref > </ interceptor-stack > </ interceptors > < action name = "demo" class = "com.action.LoginAction" > < result name = "error" type = "redirect" >/error.jsp</ result > < result name = "success" >/success.jsp</ result > < result name = "checkError" >/checkSession.jsp</ result > < interceptor-ref name = "defaultstack1" ></ interceptor-ref > </ action > </ package > |
實例中使用了interceptor-stack來定義了一個名稱為defaultstack1的攔截器棧,在該棧中添加了要執行的攔截器,把攔截器做了封裝,在Action中直接調用該攔截器棧即可,實現了攔截器棧的共享。
4、默認攔截器棧
另外可以定義默認的攔截器棧,即:如果某個Action中沒有定義攔截器,那么它會默認執行該公共的攔截器。它和interceptors標簽屬于同一等級的,使用default-interceptor-ref定義。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
< package name = "testLogin" namespace = "/" extends = "struts-default" > <!-- 攔截器 --> < interceptors > < interceptor name = "myInterceptor" class = "com.interceptor.MyInterceptor" ></ interceptor > <!-- 定義公共的攔截器鏈,在action標簽中只需要引用攔截器鏈 --> < interceptor-stack name = "defaultinter" > < interceptor-ref name = "myInterceptor" ></ interceptor-ref > < interceptor-ref name = "defaultStack" ></ interceptor-ref > </ interceptor-stack > </ interceptors > <!-- 定義默認的攔截器棧,會自動注冊到action中 --> < default-interceptor-ref name = "defaultinter" ></ default-interceptor-ref > < action name = "demo" class = "com.action.LoginAction" > < result name = "error" type = "redirect" >/error.jsp</ result > < result name = "success" >/success.jsp</ result > < result name = "checkError" >/checkSession.jsp</ result > </ action > </ package > |
定義的默認的攔截器棧只是在Action沒有指定攔截器的情況下才執行自定義默認的攔截器棧的,如果在Action中重定義了攔截器,那么它會覆蓋自定義默認的攔截器棧的。
5、不執行任何攔截器
還有一種情況是一個package中定義了默認的攔截器,但是在編寫的某個Action中并不需要執行任何攔截器,那么此時可以在相應的Action中添加一個名稱為defaultStack的攔截器即可,它是系統默認的攔截器,不會有任何操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
< package name = "testLogin" namespace = "/" extends = "struts-default" > <!-- 攔截器 --> < interceptors > < interceptor name = "myInterceptor" class = "com.interceptor.MyInterceptor" ></ interceptor > <!-- 定義公共的攔截器鏈,在action標簽中只需要引用攔截器鏈 --> < interceptor-stack name = "defaultinter" > < interceptor-ref name = "myInterceptor" ></ interceptor-ref > < interceptor-ref name = "defaultStack" ></ interceptor-ref > </ interceptor-stack > </ interceptors > <!-- 定義默認的攔截器棧,會自動注冊到action中 --> < default-interceptor-ref name = "defaultinter" ></ default-interceptor-ref > < action name = "demo" class = "com.action.LoginAction" > < result name = "error" type = "redirect" >/error.jsp</ result > < result name = "success" >/success.jsp</ result > < result name = "checkError" >/checkSession.jsp</ result > <!-- 添加defaultStack保證不執行攔截器 --> < interceptor-ref name = "defaultStack" ></ interceptor-ref > </ action > </ package > |
6、攔截方法
6.1 用法
上面的攔截器只是實現了對Action的攔截,其實攔截器的功能很強大,它也可以攔截相應Action方法。和攔截Action不同的是想要攔截方法就必須繼承類MethodFilterInterceptor,該類封存在xwork-core.jar中,又一次證明了WebWork是Struts2的核心。另外還需要在配置文件中添加相應的屬性來確定攔截的方法和不攔截的方法,具體配置方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
< package name = "testLogin" namespace = "/" extends = "struts-default" > <!-- 攔截器 --> < interceptors > < interceptor name = "myInterceptor" class = "com.interceptor.MyInterceptor" ></ interceptor > <!-- 定義公共的攔截器鏈,在action標簽中只需要引用攔截器鏈 --> < interceptor-stack name = "defaultinter" > < interceptor-ref name = "myInterceptor" ></ interceptor-ref > < interceptor-ref name = "defaultStack" ></ interceptor-ref > </ interceptor-stack > </ interceptors > < action name = "demo" class = "com.action.LoginAction" > < result name = "error" type = "redirect" >/error.jsp</ result > < result name = "success" >/success.jsp</ result > < result name = "checkError" >/checkSession.jsp</ result > <!-- 在defaultStack中配置攔截方法,參數includeMethods中添加被攔截的方法名稱,excludeMethods中添加不需要攔截的名稱 --> < interceptor-ref name = "defaultStack" > < param name = "includeMethods" >添加要攔截的方法名稱</ param > <!-- 攔截方法 --> < param name = "excludeMethods" >添加不需要攔截的方法名稱</ param > <!-- 不攔截方法 --> </ interceptor-ref > </ action > </ package > |
繼承MethodFilterInterceptor類的相應攔截方法的類中的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package com.interceptor; import java.util.Map; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; public class inter extends MethodFilterInterceptor { @Override public String doIntercept(ActionInvocation invocation) throws Exception { System.out.println( "--intercept()--" ); //獲取相應的Session Map<String,Object> session=invocation.getInvocationContext().getSession(); Map request=(Map)ActionContext.getContext().get( "request" ); String username=(String)request.get( "user.username" ); if (session.get( "username" ) != null ){ String result=invocation.invoke(); System.out.println( "--end()--" ); return result; } } } |
6.2 Demo
來看一個攔截方法的實例,并對結果進行分析。下面的實例演示攔截方法的輸出結果,在實例中分別創建了一個loginAction類,添加Action要執行的方法;Inter類,攔截器中重寫MethodFilterInterceptor方法,在控制臺中輸出是否對某個方法進行攔截;login.jsp文件,添加三個按鈕,分別演示三個方法的執行。
(1)struts.xml內方法攔截器的定義,在package中定義了一個名稱為inter的攔截器,在攔截器中指定了參數,includeMethods用來攔截Method1,excludeMethods中的Method2表示不攔截Methods2方法,具體配置如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<!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.action.extension" value = "," ></ constant > < package name = "login" extends = "struts-default" > < interceptors > < interceptor name = "inter" class = "com.interceptor.inter" > < param name = "includeMethods" >Method1</ param > <!-- 攔截Method1方法 --> < param name = "excludeMethods" >Method2</ param > </ interceptor > < interceptor-stack name = "myInterceptor" > < interceptor-ref name = "inter" ></ interceptor-ref > < interceptor-ref name = "defaultStack" ></ interceptor-ref > </ interceptor-stack > </ interceptors > < action name = "loginaction" class = "com.action.loginAction" > < result name = "success" >success.jsp</ result > < result name = "error" >error.jsp</ result > < result name = "cancel" type = "redirectAction" >Welcome</ result > < interceptor-ref name = "inter" ></ interceptor-ref > < interceptor-ref name = "defaultStack" ></ interceptor-ref > </ action > </ package > </ struts > |
(2)loginAction類,配置login.jsp中的action,分別在該類中添加Method1-Method3三個方法,其中Method1被攔截,Method2和Method3不被攔截,最后我們查看輸出結果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
package com.action; import com.opensymphony.xwork2.ActionSupport; public class loginAction extends ActionSupport { @Override public String execute() throws Exception { if ( this .username.equals( "admin" ) && this .password.equals( "admin" )){ return "success" ; } else if ( this .username.equals( "cancel" ) && this .password.equals( "cancel" )){ return "cancel" ; } else { return "error" ; } } public void Method1(){ System.out.println( "執行方法:Method1" ); } public void Method2(){ System.out.println( "執行方法:Method2" ); } public void Method3(){ System.out.println( "執行方法:Method3" ); } private String username; private String password; public String getUsername(){ return this .username; } public void setUsername(String username){ this .username=username; } public String getPassword(){ return this .password; } public void setPassword(String password){ this .password=password; } } |
(3)inter類,繼承MethodFilterInterceptor類,用來實現攔截方法。重寫doIntercept方法,在該方法中添加攔截的相應信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.interceptor; import java.util.Date; import java.util.Map; import com.action.loginAction; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; public class inter extends MethodFilterInterceptor { @Override protected String doIntercept(ActionInvocation invocation) throws Exception { // TODO Auto-generated method stub System.out.println( "攔截器在Action執行前攔截" + new Date()); String result=invocation.invoke(); //執行Action方法 System.out.println( "攔截器在Action執行后攔截" + new Date()); return result; } } |
(4)login.jsp,在jsp頁面上添加三個按鈕,分別演示三個方法,判斷攔截器對方法的攔截情況。三個按鈕在點擊后回發的action是在javascript中動態的進行添加的,這樣做達到了一個form中執行不同的action的方法,當然還有其它的方法,將會在下篇文章中討論。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> < html > < head > < meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" > < title >Insert title here</ title > < script type = "text/javascript" > //方法1,定義被攔截的方法的實例 function method1(){ var form=document.forms[0]; form.action="loginaction!Method1"; form.submit(); } //方法2,為按鈕2添加不攔截的方法 function method2(){ var form=document.forms[0]; form.action="loginaction!Method2"; form.submit(); } //方法3,為按鈕3添加不攔截的方法 function method3(){ var form=document.forms[0]; form.action="loginaction!Method3"; form.submit(); } </ script > </ head > < body > < form > 用戶名:< input type = "text" name = "username" >< br > 密 碼:< input type = "text" name = "password" >< br > < input type = "submit" name = "ok" value = "按鈕1" onclick = "method1()" > < input type = "submit" name = "ok1" value = "按鈕2" onclick = "method2()" > < input type = "submit" name = "ok2" value = "按鈕3" onclick = "method3()" > </ form > </ body > </ html > |
運行完成后的頁面視圖:
(5)分析運行結果,分別單擊按鈕1、2、3,在控制臺中輸出結果,按鈕1是綁定的method1,該方法在struts.xml中進行了攔截如果結果正確的話會顯示被攔截的結果,而相應的按鈕2和3只輸出運行結果,因為它們沒有被攔截。那看下面的結果圖:
結果圖正好正是了我們的分析結果,按鈕1被攔截了,執行了inter類中的doIntercept方法,二相應的按鈕2和3沒有被攔截。也就是說,Method1被放到了方法攔截器的白名單內,執行要攔截該方法;Method2被放到了攔截器黑名單內,不需要攔截該方法;Method3不做任何處理。
7、結語
對于攔截器的內容就總結到這里,攔截器提供了很強大的功能,使得開發人員能夠在運行時控制輸出結果,增加了編程的靈活性。另外對于任何理論性的東西都不要試圖去記憶,一定要理性的去分析,多多實踐,動手做幾個實例,分析結果更深刻的理解。
PS:Struts2(XWork)提供的攔截器的功能說明
攔截器 |
名字 |
說明 |
Alias Interceptor |
alias |
在不同請求之間將請求參數在不同名字件轉換,請求內容不變 |
Chaining Interceptor |
chain |
讓前一個Action的屬性可以被后一個Action訪問,現在和chain類型的result(<result type=”chain”>)結合使用。 |
Checkbox Interceptor |
checkbox |
添加了checkbox自動處理代碼,將沒有選中的checkbox的內容設定為false,而html默認情況下不提交沒有選中的checkbox。 |
Cookies Interceptor |
cookies |
使用配置的name,value來是指cookies |
Conversion Error Interceptor |
conversionError |
將錯誤從ActionContext中添加到Action的屬性字段中。 |
Create Session Interceptor |
createSession |
自動的創建HttpSession,用來為需要使用到HttpSession的攔截器服務。 |
Debugging Interceptor |
debugging |
提供不同的調試用的頁面來展現內部的數據狀況。 |
Execute and Wait Interceptor |
execAndWait |
在后臺執行Action,同時將用戶帶到一個中間的等待頁面。 |
Exception Interceptor |
exception |
將異常定位到一個畫面 |
File Upload Interceptor |
fileUpload |
提供文件上傳功能 |
I18n Interceptor |
i18n |
記錄用戶選擇的locale |
Logger Interceptor |
logger |
輸出Action的名字 |
Message Store Interceptor |
store |
存儲或者訪問實現ValidationAware接口的Action類出現的消息,錯誤,字段錯誤等。 |
Model Driven Interceptor |
model-driven |
如果一個類實現了ModelDriven,將getModel得到的結果放在Value Stack中。 |
Scoped Model Driven |
scoped-model-driven |
如果一個Action實現了ScopedModelDriven,則這個攔截器會從相應的Scope中取出model調用Action的setModel方法將其放入Action內部。 |
Parameters Interceptor |
params |
將請求中的參數設置到Action中去。 |
Prepare Interceptor |
prepare |
如果Acton實現了Preparable,則該攔截器調用Action類的prepare方法。 |
Scope Interceptor |
scope |
將Action狀態存入session和application的簡單方法。 |
Servlet Config Interceptor |
servletConfig |
提供訪問HttpServletRequest和HttpServletResponse的方法,以Map的方式訪問。 |
Static Parameters Interceptor |
staticParams |
從struts.xml文件中將<action>中的<param>中的內容設置到對應的Action中。 |
Roles Interceptor |
roles |
確定用戶是否具有JAAS指定的Role,否則不予執行。 |
Timer Interceptor |
timer |
輸出Action執行的時間 |
Token Interceptor |
token |
通過Token來避免雙擊 |
Token Session Interceptor |
tokenSession |
和Token Interceptor一樣,不過雙擊的時候把請求的數據存儲在Session中 |
Validation Interceptor |
validation |
使用action-validation.xml文件中定義的內容校驗提交的數據。 |
Workflow Interceptor |
workflow |
調用Action的validate方法,一旦有錯誤返回,重新定位到INPUT畫面 |
Parameter Filter Interceptor |
N/A |
從參數列表中刪除不必要的參數 |
Profiling Interceptor |
profiling |
通過參數激活profile |