Struts2攔截器詳解
Struts2 攔截器
1.Struts中攔截器就是一個類,實現了Interceptor 接口的一個類。
2.Struts中攔截器和Servlet中的Filter有類似的功能,從字面意思來看,Struts 攔截器就是在目標對應執行之前或之后做一些事情,其實Struts中的攔截器的實現也是一樣,在具體Action的被調用之前或之后可以做一些操作,采用配置化的方法進行管理,使用起來比較簡單。但是我們在學習過程中最好去了解攔截底層實現原理,以便在使用過程中能夠控制自己的程序。從了解攔截器的實現原理入手會是我們學習攔截器的最好途徑。
3.攔截器采用的動態代理模式實現的在目標執行之前或之后插入必要的輔助業務。其實采用的是一種AOP的思想,來降低系統耦合。
下面我們介紹一個動態代理:
Java中提供一種動態代理的實現模式,我們用一個例子實現動態代理的演示:
涉及到代理就肯定要有:目標對象 代理對象 攔截器
三者之間的關系: 代理對象 代理 目標對象 在目標對象執行之前或之后 加入攔截器的功能
首先我們創建一個目標對象:
3.1 定義一個接口
package com.snt.struts2.interceptor; public interface ITargetInterface { public void doSomething(); }
3.2 定義一個目標對象實現目標對象接口 [目標對象一定要實現一個接口]
package com.snt.struts2.interceptor; public class Target implements ITargetInterface { // 攔截的目標對象 public void doSomething() { System.out.println("do something..."); } }
3.3 定義一個攔截器(這里我們的攔截器比較簡單,就是一個普通在類,定義了一在目標對象調用之前工之后要執行的操作) [定義的兩個方法,代表在目標對象調用之前和之后要執行的操作]
package com.snt.struts2.interceptor; public class Interceptor { public void befor() { System.out.println("before"); } public void after() { System.out.println("after"); } }
3.4 下面來實現代理,如何為目標對象產生一個代理對象呢?java為我們提供了一定代理機制!
Java在java.lang.reflect包下面提供了一個Proxy 類,這個類可以為一個目標類產生代理類。
使用以下更簡單的方法:
ITargetInterface iTargetInterface = (ITargetInterface) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { ITargetInterface.class }, handler);
第一個參數:目標類的加載器 第二個參數:目標實現的接口集合 第三個參數:代理類的調用處理程序對象.
動態代理類(以下簡稱為代理類)是一個實現在創建類時在運行時指定的接口列表的類,該類具有下面描述的行為。代理接口 是代理類實現的一個接口。代理實例 是代理類的一個實例。 每個代理實例都有一個關聯的調用處理程序 對象,它可以實現接口InvocationHandler。通過其中一個代理接口的代理實例上的方法調用將被指派到實例的調用處理程序的Invoke方法,并傳遞代理實例、識別調用方法的 java.lang.reflect.Method 對象以及包含參數的 Object 類型的數組。調用處理程序以適當的方式處理編碼的方法調用,并且它返回的結果將作為代理實例上方法調用的結果返回。
對于我們這個例子:也要先創建一個代理類的調用處理程序,如下:
3.5 創建一個MyHandler類,實現InvocationHandler接口,實現其中的invoke()方法,代碼如下:
package com.snt.struts2.interceptor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyHandler implements InvocationHandler { private Object obj;// 目標對象 // 攔截器對象 private Interceptor interceptor = new Interceptor(); // 注入目標對象 public void setObject(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; // 調用目標方法前執行的操作 interceptor.befor(); result = method.invoke(obj, args); // 調用目標方法后執行的操作 interceptor.after(); return result; } }
3.6 創建一個產生代理的工廠類,創建一個MyProxy類,代碼如下:
package com.snt.struts2.interceptor; import java.lang.reflect.Proxy; /** * 產生一個代理 * @author Wang * */ public class MyProxy { // 根據一個目標類產生一個代理對象 public Object getProxy(Object object) { MyHandler mh = new MyHandler(); mh.setObject(object); return Proxy.newProxyInstance(Target.class.getClassLoader(), object .getClass().getInterfaces(), mh); } }
創建一個測試類,為目標對象產生代理,調用其方法,查看執行效果:
package com.snt.struts2.interceptor; public class Client { public static void main(String[] args) { ITargetInterface target = new Target(); MyProxy mp = new MyProxy(); ITargetInterface proxy = (ITargetInterface) mp.getProxy(target); proxy.doSomething(); } }
運行結果:在調用目標類方法doSomethin()之前和之后插入攔截的功能。
4.OK,上面講的一個采用我們自定義攔截器的方式實現在目標攔截的功能,下面的我們看一下Struts2中如何使用攔截器。Struts2中攔截器是一個實現了Interceptor接口的類,下面我們來定義一具體的攔截器,實現Action的攔截。
定義一個MyInterceptor 類,代碼如下:
package com.snt.struts2.interceptors; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class MyInterceptor implements Interceptor { private String hello; public String getHello() { return hello; } public void setHello(String hello) { this.hello = hello; } public void destroy() { System.out.println("destory()"); } public void init() { System.out.println("init()"); System.out.println(hello); } /** * * 攔截Action */ public String intercept(ActionInvocation invocation) throws Exception { System.out.println("interceptor"); String result = invocation.invoke(); System.out.println("finshed"); return result; } }
攔截器中主要實現interceptor()方法,在interceptor中調用目標對象前后加入你定義的業務操作。
OK,攔截器配置好之后,如何讓其生效呢?需要在struts.xml 文件中配置。
在struts.xml 配置文件中,Struts2會為每個Action加上一具默認在攔截器配置,那就是struts-default.xml中的<interceptor-ref name="defaultStack" />,在Struts2攔截器存在兩種概念,一種是攔截器,一種攔截鏈,攔截鏈就是一系列的攔截器連接在一起進行對目標攔截,而Struts2并沒有把兩種概念進行區別,而是都它們都當成攔截器對待,因此在攔截器的定義標簽<interceptors></interceptors>中,定義、引用攔截器和攔截鏈是同樣的方式。
對于上面的攔截器,我們需要在struts.xml 文件中如下配置
方式1:
<?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> <package name="struts2" extends="struts-default"> <!-- 配置攔截器 --> <interceptors> <!-- 自定義攔截器 --> <interceptor name="myInterceptor" class="com.snt.struts2.interceptors.MyInterceptor"> <param name="hello">world</param> </interceptor> </interceptors> <action name="point" class="com.snt.struts2.action.PointAction"> <result name="success">output.jsp</result> <result name="input">input.jsp</result> <!-- 引用上面配置的攔截器 --> <interceptor-ref name="myInterceptor"> <param name="hello">welcom</param> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package> </struts>
我們將一個攔截配置在point這個Action中,說明我們要使用這個攔截器來攔截這個Action,見紅色的字體,攔截器配置中間我們還加了一個參數,struts2會自動將該參數的值加載到攔截器中對應屬性的值。OK,這樣配置好了,運行程序測試,發現結果數據是錯誤的。這是為什么呢?
前面說過Struts2會為每個Action應用一個默認的攔截器,這個攔截器是在struts-default.xml文件中配置的,而我們的struts.xml 開關又出現這樣的配置
<package name="struts2" extends="struts-default">
說明我們的配置繼承了struts-default.xml 中的配置。
當我們如下配置文件中那樣配置后,默認的配置就會被引用了,這是Struts2本身所有實現的,當我們為一個Action配置了攔截器時,默認的攔截器就會失效,但是Struts2本身的一些功能,比如說參數自動賦值又是依賴配置的默認攔截器實現,所有應用程序會出錯。這時需要我們手動將默認的攔截器引用進來,再為這項Action加入默認攔截器:
<interceptor-ref name="defaultStack"/>
另外要注意:如果攔截器聲明時和引用時都配置了參數,那最引用時的參數配置是有效的。
比如:上面的配置在配置攔截器時給hello參數傳一個”world”,但在下面action中使用攔截器時,又給參數hello配置了"welcome”值,最終使用的將是action使用時攔截器時配置的值。
方式2:
我們為簡便,還可以定義一個攔截鏈,上面說過,攔截鏈和攔截器一樣,所以我們也在<interceptors></interceptors>標簽中配置攔截鏈。
<?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> <package name="struts2" extends="struts-default"> <!-- 配置攔截器 --> <interceptors> <!-- 自定義攔截鏈 --> <interceptor-stack name="myStack"> <!-- 執行順序與配置順序有關 --> <interceptor-ref name="myInterceptor" class="com.snt.struts2.interceptors.MyInterceptor"></interceptor-ref> <interceptor-ref name="myInterceptor2" class="com.snt.struts2.interceptors.MyInterceptor2"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 設置默認攔截鏈,一個package下只能配置一個,應用所有的沒有配置攔截器或攔截鏈的Action --> <default-interceptor-ref name="myStack"></default-interceptor-ref> <action name="point" class="com.snt.struts2.action.PointAction"> <result name="success">output.jsp</result> </action> </package> </struts>
上面我們配置一個攔截鏈,但是我們并沒有寫在下面的action中,運行程序PointAction還是被攔截了。這是為什么呢?看上面有這樣一句配置:
<default-interceptor-ref name="myStack"></default-interceptor-ref>
這句配置是定義了一個默認攔截鏈或攔截器,這個攔截就像struts-default.xml文件中的defaultStack一樣,我們在我們的配置中也可以定義一個默認攔截器來攔截所有的action。但是還是要注意一點,當一個action引用了其它的攔截器或攔截鏈時,我們定義的攔截器也會失效。而且默認的攔截器一個包中只能聲明一個。
還要注意的是:我們定義多個攔截器后它們的執行順序,這個順序和他們在配置中聲明的順序是有關的,先聲明的先執行。
攔截器中執行的操作分兩種,一種是目標對象調用前執行的操作,一種目標對象調用后執行的操作。這個也是有順序的。如果有兩個攔截器,那么執行的順序就是:第一個攔截器執行前操作——第二個攔截器執行前操作——目標對象的調用——第二個攔截器執行后操作——第一個攔截器執行后操作。
比如一個action配置了A、B兩個攔截器:攔截器A beforeA() afterA() 攔截器B beforeB() afterB()
上面代表攔截器A要在目標對象執行前調用beforeA(), 在目標對象調用后執行afterA(),攔截器B同樣。那么執行過程攔截器的調用情況如下:
beforeA()
beforeB()
目標對象的調用
afterB()
afterA()
上面的我們定義的攔截器類MyInterceptor里面有三個方法(這是Interceptor接口中的三個方法),即:
init();
destory();
interceptor(ActionInvocation invocation
) 【我們真正要實現的方法】
但是init()和destory()方法我們不經常用,但是雙必須把它繼承下來,看起不是很爽,查看API我們會找到一個AbstrctInterceptor類,這個類是一個抽象類,它實現了init()和destory()【其實兩個方法并沒有沒真正做什么,是空實現】,interceptor() 方法是抽象的,所以我通常自定義攔截器時,可以實現AbstractInterceptor類,著重寫interceptor()方法即可。
OK,上面講的都是針對Action的攔截,Struts2中還有一種攔截器是針對方法的攔截器。
Struts2中有一個MethodFilterInterceptor的攔截器,它繼承了AbstractInterceptor類,可以對方法進行攔截。這個類也是個抽象類,其中有doInterceptor()方法是個抽象方法,我們只需要實現這個方法即可。
比如:我們需要使用一個攔截器攔截一個action中的login()方法,而不攔截器action中的logout()方法,看攔截器如何實現:
MethodFileterInterceptor 攔截器是通過指定
// 不需要攔截的方法名列表 protected Set<String> excludeMethods = Collections.emptySet(); // 需要攔截的方法名列表 protected Set<String> includeMethods = Collections.emptySet();
以上兩個屬性,一個是指定哪些方法需要攔截,另一個是指定哪些方法不需要攔截。在配置攔截器時需要配置這兩個參數。
我們行定義一個方法攔截器,代碼如下:MyInterceptor3
package com.snt.struts2.interceptors; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; public class MyInterceptor3 extends MethodFilterInterceptor { @Override protected String doIntercept(ActionInvocation invocation) throws Exception { System.out.println("my Interceptor3"); String result = invocation.invoke(); return result; } }
下面我們配置這個方法攔截器:
<?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> <package name="struts2" extends="struts-default"> <!-- 配置一個攔截器 --> <interceptors> <!-- 自定義攔截器 --> <interceptor name="myInterceptor3" class="com.snt.struts2.interceptors.MyIntercepto3" /> <interceptor name="myInterceptor" class="com.snt.struts2.interceptors.MyInterceptor"> <param name="hello">world</param> </interceptor> <!-- 自定義攔截鏈 --> <interceptor-stack name="myStack"> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 設置默認攔截鏈,一個package下只能配置一個,應用所有的沒有配置攔截器或攔截鏈的Action --> <default-interceptor-ref name="myStack"></default-interceptor-ref> <action name="point" class="com.snt.struts2.action.PointAction"> <interceptor-ref name="myStack" /> <interceptor-ref name="myInterceptor3"> <!-- 同時指定攔截方法和排除方法時,攔截方法的級別高 --> <param name="includeMethods">execute,login</param> <param name="excludeMethods">logout</param> </interceptor-ref> </action> </package> </struts>
注意:同時指定攔截方法和排除方法時,攔截方法的級別高。
上面舉的例子攔截器中東西都比較簡單,真正在開發時候需要根據具體的業務來編寫攔截器的內容!所以大家在實際項目中多多練習便會慢慢的掌握攔截器,另外Struts2中自帶了的攔截器、攔截鏈(在struts-default.xml中),大家還是應該了解下的,具體如下:
攔截器 |
名字 |
說明 |
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 |
那么關于攔截器的介紹就到這里了,希望對大家能有所幫助