Struts2 ---攔截器的理解

天才攻城獅發表於2017-03-10
        攔截器(interceptor)是Struts2最強大的特性之一,也可以說是struts2的核心,攔截器可以讓你在Action和result被執行之前或之後進行一些處理。同時,攔截器也可以讓你將通用的程式碼模組化並作為可重用的類。
Struts2中的很多特性都是由攔截器來完成的。攔截是AOP的一種實現策略。攔截器是動態攔截Action呼叫的物件。它提供了一種機制可以使開發者可以定義在一個action執行的前後執行的程式碼,也可以在一個action執行前阻止其執行。同時也是提供了一種可以提取action中可重用的部分的方式。
我個人的理解是在Action和result執行前後新增新的功能.
比如在登入一個頁面時,如果要求使用者密碼、許可權等的驗證,就可以用自定義的攔截器進行密碼驗證和許可權限制。對符合的登入者才跳轉到正確頁面。這樣如果有新增許可權的話,不用在action裡修改任何程式碼,直接在interceptor裡修改就行了。
/////////////////////////////以下為轉載
看了浪曦的STRUTS2的視訊之後,才發現攔截器是多麼滴好用,以下是轉載內容: 
攔截器的作用主要就是攔截東西,攔截什麼呢?當然是'action'了,在執行'action'之前 攔截器會起作用,執行一些預先處理的程式碼, 
接著區執行action中相關的方法,之後,流程又會回到攔截器裡面,接著去執行後續的一些操作。 

先看配置,這些配置都是在struts.xml裡面的。配置相當簡單,不過底層都是很複雜的。在這章之前,我專門看了下動態代理,感覺真的是開發者花了不少的心血, 

首先先建一個包,放我們的interceptor。 
過濾器和攔截器是非常相似的,public interface Filter類裡面有三個方法: 
init(FilterConfig filterConfig),destroy(),doFilter(ServletRequest request,ServletResponse response,FilterChain chain), 
這裡面的doFilter()方法是最重要的,在struts2中就相當於攔截的那個方法。 

先寫一個攔截器,在struts2中要實現一個介面 這個介面是什麼呢?在哪呢?是webwork是我們以前聽的最多的關於攔截器的框架, 
struts2用了其中一個核心的東西,這個東西在是什麼呢?是xwork恩,有了它才可以攔截,好了我們在哪找呢?
在com.opensymphony.xwork2.interceptor中找,裡面有個Interceptor 這是個介面,裡面也有三個方法, 
有init,destroy和intercept三個方法,而在struts2裡面的所有的攔截器都繼承這個介面! 

就依照這個介面寫一個攔截器類,呵呵! 
package com.interceptor; 
import com.opensymphony.xwork2.ActionInvocation; 
import com.opensymphony.xwork2.interceptor.Interceptor; 

public class MyInterceptor implements Interceptor{ 
    private String hello;//一定要寫 
    get和set方法 
    public void destroy() { 
       System.out.println("destory"); 
    } 
    public void init() { 
       System.out.println("init"); 
    } 
    public String intercept(ActionInvocation invoker) throws Exception { 
       System.out.println("intercept"); 
       String result=invoker.invoke(); 
    //  System.out.println("finish1"); 
       return result; 
    } 


為了看這些是怎麼實現的,加入了一些列印! 
Intercept方法返回一個字串,這個裡面最重要的是ActionInvocation 也是個抽象的介面,裡面有個invoke() 方法 
          作用:Invokes the next step in processing this ActionInvocation. 即呼叫下一個攔截器,如果有的話! 
為了,讓struts2知道我們寫了個攔截器,就在struts。Xml中配置一下。 
<package name="struts2" extends="struts-default"> 
<interceptors> 
   <interceptor name="myinterceptor" class="com.interceptor.MyInterceptor"> 
              <param name="hello">world</param> 
           </interceptor> 
</interceptors> 

註釋:action是寫的一個註冊頁面,也可以隨便用個action 在這個點上是不影響需要檢驗結果的 

<action name="register" class="com.test.action.RegisterAction" > 
           <result name="input">/register.jsp</result> 
           <result name="success">/success.jsp</result> 
</package> 

好了到了這裡 攔截器也配置好了,但是攔截器是攔截action的,怎麼才能讓action被攔截呢? 
一般的情況下,放在result後面 怎麼寫呢?好了,如下: 
<interceptor-ref name="myinterceptor"> 
</interceptor-ref> 

這樣就可以讓aciton被攔截了,到此,好了,可以執行程式了: 
輸出結果是:啟動伺服器init被打出 
執行後提交action 輸出intercept 
這個就是初步的一個攔截器。 



=======在此可能出現一個問題,是什麼呢?如果就我們做的登陸而言,當然登陸大家都做的很多了,可以想一下,有資料轉換,有驗證資料是否符合我們的要求==== 

Ok 如果按照上述執行的話,當資料轉換,驗證出錯的時候,就不會有提示,為什麼呢? 這裡就和struts2的預設攔截器有關係! 

這裡可以開啟一個檔案看一下,看了就會明白,是什麼檔案呢?struts-default。Xml 
這裡定義的很多的東西,和我們的程式相關的非常緊密 
首先這裡有個 
<package name="struts-default" abstract="true">和struts.Xml裡面的 
<package name="struts2" extends="struts-default">有什麼一樣呢?很明顯可以猜到struts.Xml中繼承的就是default。Xml中的struts-default。 
這個裡面還有個<interceptors>和</interceptors>這個是定義攔截器的,仔細看可以發現裡面有個validation 如此可以猜想, 
validation 也是定義的一個攔截器,可是最後當登陸出錯後沒有提示資訊呢?肯定是相關的東西沒有執行,以前的執行了,現在沒執行, 
在新加了個攔截器後,這樣說明了,是現有的攔截器取代了原有的攔截器,這個是我僅能想到的!結果就是這個樣子的。那麼我們手工吧把原來的預設攔截器加入,這樣可以嗎? 
答案是可以的!新增如下! 

接著上面result後面新增一個把! 
<interceptor-ref name="defaultStack"></interceptor-ref> 

這樣還可以說明:如果我們沒有新增攔截器的話,預設的攔截器會自動新增到裡面。 


攔截器棧 
過濾器可以組成過濾器鏈,就是可以有多個過濾器來去過濾一個元件,攔截器也是,只不過是叫攔截器棧(相當於串攔截器)。 
攔截器棧先把攔截器逐個執行,接著執行action方法,之後又按照相反的順序回到最後的一個攔截器,再回到檢視。 

攔截器棧是怎麼構成的呢?繼續看struts-default.Xml這個檔案!裡面有這些東西: 
            <interceptor-stack name="defaultStack"> 
                <interceptor-ref name="static-params"/> 
                <interceptor-ref name="params"/> 
                <interceptor-ref name="conversionError"/> 
            </interceptor-stack> 

  <interceptor-stack name="validationWorkflowStack"> 
                <interceptor-ref name="defaultStack"/> 
                <interceptor-ref name="validation"/> 
                <interceptor-ref name="workflow"/> 
           </interceptor-stack> 

這裡面看見了 棧是什麼樣的結構,是由很多個預先定義好的攔截器構成,而且也可以再加上攔截器棧組成,就如此就組成了! 



還有這行程式碼: 
<default-class-ref class="com.opensymphony.xwork2.ActionSupport" /> 
這個是定義預設的攔截器,竟然是預設的當然只可能有一個!是把! 

好了,到了這裡,就來有進一步學習struts2的攔截器把!讓我們自己配置自己的攔截器棧! 
在開始 的時候我們新增的第一個myinterceptor攔截器的時候我新增了一個引數 

<param name="hello">world</param> 這裡我們可以通過配置檔案,給攔截器新增一個引數,那這個在攔截器中怎麼取得的呢? 
瞭解過濾器的朋友都知道,裡面有個init(FilterConfig filterConfig)方法這樣可以取值,而在struts2中沒有這麼麻煩,做法嗎, 
上面其實已經給出來了! 
private String hello; 
    get和set方法 

寫個變數,然後加上get和set方法,當然變數的名字必須和設定的引數是相同的,這個是賦值成功的前提條件 

此時,執行,成功輸出:world   這裡的引數使用是在定義攔截器的時候,還有一種是在使用攔截器的時候新增引數。怎麼做呢? 

定義攔截器的情況: 

<interceptor name="myinterceptor" class="com.interceptor.MyInterceptor"> 
              <param name="hello">world</param> 
</interceptor> 

使用攔截器的時候的情況: 
<interceptor-ref name="myinterceptor"> 
              <param name="hello">zhuxinyu</param> 
</interceptor-ref> 

這下知道了嗎,還有,當出現如上引用的時候給同樣的引數設定了不同的值,會顯示那一個呢,在執行的時候? 
結果是:zhuxinyu  很明顯 覆蓋了第一個  結論是:使用的時候比定義的時候更加厲害,這叫撒,縣官不如現管! 
好了,把這些零散的東西搞完了,真的開始解決更多的知識! 

攔截器棧,在struts2裡面,其實是把攔截器和攔截器棧一樣的對待。可以把攔截器棧當作一個攔截器看待,同樣的引用。 
現在定義一個攔截器棧把! 

同樣在<interceptors> </interceptors>裡面定義 

<interceptor-stack name="mystack"> 
           <interceptor-ref name="myinterceptor"></interceptor-ref> 
           <interceptor-ref name="defaultStack"></interceptor-ref> 
</interceptor-stack> 

看見了沒,一個引用第一次定義的myinterceptor攔截器,一個引用預設的攔截器,怎麼引用呢,和開始的一個樣,呵呵! 

<interceptor-ref name=" mystack "> 
</interceptor-ref> 

呵呵 ,這樣看是不是比剛才簡單呢?把兩次引用換成一次!執行結果呢?和上次一樣,而且都成功! 

當然我們也可以自己定義一個預設的攔截器,這樣在程式中怎麼做呢?呵呵,定義如下 
<default-interceptor-ref name="mystack"></default-interceptor-ref> 
這裡引用的就是上面的mystack攔截器,這樣在程式中如果不在action中加入攔截器,它同樣可以執行相應的工作, 
前面已經說過了,如果不加入任何攔截器的引用,它將把預設的攔截器加入。 


我們在做攔截器的時候,剛才實現了Interceptor介面,裡面有三個方法,但是一般的情況下init()和destroy()方法我們用不上, 
最關心的就是intercept(ActionInvocation invoker){}方法,所以怎麼辦呢?其實,struts2給我們提供了一個簡化的攔截器類,這個是什麼呢? 

MethodFilterInterceptor  這是一個抽象的類,裡面實現了init()和destroy()方法,所以只要我們繼承這個類,就不用再多寫這兩個方法! 

為了驗證是對的,就寫了個方法,實現如下: 

public class MyInterceptor2 extends AbstractInterceptor{ 
  protected String doIntercept(ActionInvocation invocation) throws Exception { 
       System.out.println("my interceptor2"); 
       String result=invocation.invoke(); 
       return result; 
    } 


就是這樣一個簡單的東西,呵呵,把它和其他的攔截器一樣的配置,執行,呵呵就可以出來結果了! 
當然在這裡還需要指出一點,你安放的攔截器的順序,其實也就是攔截器執行的順序!但是攔截器,不只是在執行execute()方法之前要執行, 
而且在execute()方法之後也要執行。給出如下兩個攔截器說明: 


public String intercept(ActionInvocation invoker) throws Exception { 
       System.out.println("intercept1"); 
       String result=invoker.invoke(); 
       System.out.println("finish1"); 
       return result; 
    } 



public String intercept(ActionInvocation invoker) throws Exception { 
       System.out.println("intercept2"); 
       String result=invoker.invoke(); 
       System.out.println("finish2"); 
       return result; 
    } 

在配置順序也是一二,結果會輸出什麼呢? 

intercept1  intercept2 finish2  finish1  這裡執行攔截器的過程是正著來的,回來的時候是反著的。就像你要進一個很多門的房間一樣。 
進去一個,開一個門,為了讓自己能回來的方便一些,這個開啟的門就不要關著了,當你把所有的門進去了後,然後回來,再逐個關門。 
這樣的過程就像是這個攔截器執行的過程。 

最後講一個方法過濾攔截器,顧名思義,過濾的是方法。其實在struts2中可以在一個action類中寫很多個與aciton的execute方法類似的方法。 
只要在struts。Xml中的action新增一個屬性就可以了這個屬性是method比如: 

<action name="register"class="com.test.action.RegisterAction" method="test">當然在action類中也有個test()方法 

這個攔截器可以細化到攔截到具體的哪一個方法。如果不是方法過濾攔截器 哪麼它可能將與execute()方法類似的方法都執行。 
比如說上面的test()方法。如此這樣照成很多的不必要。於是這種攔截器就顯的格外的重要。 
在這個類繼承後實現的不是inteceptor()方法而是doIntercept(),可是做好這個類後如何配置繼承MethodFilterInterceptor這個類呢? 
如下(為了實現過濾方法加入了幾個引數,其他的都相同): 

<interceptor-ref name="myinterceptor2"> 
              <param name="includeMethods">execute,test</param> 
</interceptor-ref> 

includeMethods  包含execute,test這兩個方法,結果執行了這個攔截器,如果改成excludeMethods ,就不會執行了,也可以再加下面的一個引數 
<param name="excludeMethods">execute,test</param> 
不排除execute,test這兩個方法 可是又加入又排除到底執行嗎?答案是執行的,必定結果是最能說明問題的!
------------------------------------------------------馬士兵版對攔截器的理解----------------------------------------------

攔截器能在action被呼叫之前和被呼叫之後執行一些“程式碼”Struts2框架的大部分核心功能都是通過攔截器來實現的,如防止重複提交、型別轉換、物件封裝、校驗、檔案上傳、頁面預裝載等等,都是在攔截器的幫助下實現的。每一個攔截器都是獨立裝載的(pluggable),我們可以根據實際的需要為每一個action配置它所需要的攔截器。 

myStruts2專案下,重新對配置檔案作如下修改: <package name="myFirst" namespace="/" extends="struts-default">

<interceptors>

<interceptor name="timer"class="com.opensymphony.xwork2.interceptor.TimerInterceptor" />

<interceptor name="params"   class="com.opensymphony.xwork2.interceptor.ParametersInterceptor" />

</interceptors>

 

<action name="login" class="com.asm.LoginAction">

<interceptor-ref name="timer"></interceptor-ref>

<interceptor-ref name="params"></interceptor-ref>

<result name="loginSuccess">/success.jsp</result>

<result name="loginFailure">/failure.jsp</result>

</action>

</package>

首先在package中定義了兩個攔截器,然後在login action中引用了這兩個攔截器,需要說明的是這裡使用的攔截器都是系統自帶的攔截器。其實在extends所繼承的struts-default中就包含了很多攔截器,也包括我們這裡所用的攔截器,但如果在此action中不使用params攔截器,將會報空指標錯,因為params攔截器的作用是傳遞表單引數,如果不使用此攔截器就不能在action中得到表單引數,所以引用時會報空指標錯。雖然extends繼承的strust-default自帶有params攔截器,但是當我們自己引用了攔截器時,繼承struts-default將不會再為我們分配預設的攔截器(有點類似構造器),但是我們仍然可以通過<interceptor-ref name="defaultStack"/>來繼續使用struts-defalut的攔截器。補充:由於上面的package繼承於struts-default,而我們這裡所用到的timer和params都是在struts-defalut中定義過,所以即使我們在<interceptors>中沒有定義過這兩個攔截器,也可以直接在action中引用。

使用</interceptor-stack>組合多個攔截器:比如我們想把上面的params和timer這兩個攔截器組合:

<interceptor-stack name="timer_param">

<interceptor-ref name="timer" />

<interceptor-ref name="params" />

</interceptor-stack>

然後再在action引用<interceptor-ref name="timer_param"/>”,效果和分別引用兩個是一樣的。其實我們使用strtus-default中的<interceptor-ref name="defaultStack"/>也是使用interceptor-stack方式。

相關文章