struct2攔截器

         攔截器是Struts2的一個重要特性。Struts2框架的大多數核心功能都是通過攔截器來實現的,像避免表單重複提交、類型轉換、對象組裝、驗證、文件上傳等,都是在攔截器的幫助下實現的。

         攔截器的一個重要特徵是:它可以在Action之前調用。

         攔截器,在AOP(Aspect Oriented Programming)中用於在某個方法或字段被訪問之前,進行攔截。然後再之前或之後加入某些操作。攔截器是AOP的一種實現策略。

Struts2的攔截原理:

        當請求到達Struts2的ServletDispatcher時,Struts2會查找相對應的配置信息,並實例化攔截器對象。串成一個列表(list),最後一個一個地調用列表中的攔截器。

  • 配置攔截器

在struts.xml文件中定義攔截器只需爲攔截器指定一個攔截器名稱即可。使用<interceptor.../>元素來定義攔截器,最簡單的格式如下。

<interceptor name="攔截器名" class="攔截器類"></interceptor>  

有時,按照上面的配置便可以完成一個簡單的攔截器,但是,如果還需要配置攔截器時傳入攔截器參數,則需要在<interceptor.../>元素中使用<param.../>子元素。下面是在配置攔截器時,同時傳入攔截器參數的配置形式。

<interceptor name="攔截器名" class="攔截器類">  
   <!-- 下面元素可以出現0次,也可以出現無數次,其中name屬性指定需要設置的參數名 -->  
   <param name="參數名">參數值</param>  
</interceptor>  


  • 配置攔截器棧

        struts2還支持把多個攔截器連在一起組成一個攔截器棧,比如:需要在Action執行前同時進行安全檢查,身份驗證,數據校驗等等操作,可以將這些動作鏈接成一個攔截器棧。

攔截器棧的定義形式:


<interceptor-stack name="攔截器棧名">  
      <interceptor-ref name="攔截器一"></interceptor-ref>  
      <interceptor-ref name="攔截器二"></interceptor-ref>  
</interceptor-stack>

攔截器和攔截器棧的功能是一樣的,只不過攔截器棧定義了一組攔截器。

當然我們也可以在攔截器中定義攔截器棧,這樣都是可以的,主要是實現了代碼的複用。比如:


<interceptor name="a1" class="com.yao.SInterceptor">  
</interceptor>  
<interceptor name="a2" class="com.yao.DInterceptor">  
</interceptor>  
<interceptor name="a3" class="com.yao.Interceptor"></interceptor>  
     
   <interceptor-stack name="a4">  
      <interceptor-ref name="a1">  
         <param name="st">abc</param>  
      </interceptor-ref>  
      <interceptor-ref name="a2">  
      </interceptor-ref>  
</interceptor-stack>

  • 使用攔截器

攔截器在<package>目錄下聲明好了以後,下一步就是在Action中使用攔截器了

示例代碼如下:

<action name="user" class="com.yao.action.UserAction">  
          <result name="user">/user.jsp</result>  
          <result name="add_user">/add_user.jsp</result>  
          <interceptor-ref name="myfilter">  
              <param name="name">攔截器</param>  
              <!-- 指定execute方法不需要被攔截 -->  
              <param name="excludeMethods">execute</param>  
          </interceptor-ref>  
 </action>  

通過<interceptor標籤直接引用攔截器。

如果是使用攔截器棧,那示例代碼如下:

<action name="ge">  
       <result name="success">/success.jsp</result>  
       <interceptor-ref name="a1"></interceptor-ref>  
       <!-- 定義使用的攔截器棧,和使用攔截器沒有什麼區別,都是通過interceptor-ref標籤完成 -->  
       <interceptor-ref name="a4"></interceptor-ref>  
</action> 



  • 攔截器剖析
    • 實現自定義攔截器類
如果要開發自己的攔截器類,應該實現com.opensymphony.xwork2.interceptor.Interceptor接口,此接口的定義代碼如下:


import com.opensymphony.xwork2.ActionInvocation;  
  
public interface Interceptor {  
    //撤銷該攔截器之前的回調方法  
    void destory();  
    //初始化該攔截器的回調方法  
    void init();  
    //攔截器實現攔截的邏輯方法  
    String intercept(ActionInvocation invocation) throws Exception;  
} 


通過接口可以看出,該接口裏有三種方法。

        init():攔截器初始化之後,在該攔截器執行攔截之前,系統將回調該方法,對於每一個攔截器而言,該init()方法只執行一次。因此,該方法的方法體主要用戶打開一些一次性資源,例如數據庫資源等。

        destory():該方法與init()方法對應。在攔截實例被銷燬之前,系統將回調該攔截器的destory()方法,該方法用於銷燬在init()方法裏打開的資源。

        interceptor(ActionInvocation invocation):該方法是用戶需要實現的攔截動作。就像Action執行execute()方法一樣,interceptor會返回一個字符串作爲邏輯視圖。如果該方法直接返回了一個字符串,系統將跳轉到該邏輯視圖隊形的實際的視圖資源,不會調用被攔截的Action類。該方法的ActionInvocation參數保護那了被攔截的Action的引用,可以通過調用該參數的invoke()方法,將控制權轉給下一個攔截器,或者轉給Action的execute方法。

         除此之外,Struts2提供了一個AbstractInterceptor類,該類提供了空的init()和destory()方法的實現,也就是如果攔截器不需要申請資源,則可以無須實現這兩個方法。

注意:

         當實現了intercept(ActionInvocation invocation)方法時,可以獲得ActionInvocation參賽,這個參數又可以獲得被攔截的Action實例,一旦取得了Action實例,幾乎得到了全部的控制權。


實例剖析:

通過上面的瞭解,我們來做一個攔截器的實例

需求:客戶端用戶名密碼登陸,攔截器對其進行攔截,如果用戶名和密碼滿足條件,進入執行execute方法,如果不滿足要求,返回index.jsp頁面

建立相應的index.jsp頁面


<%@ page language="java" import="java.util.*" pageEncoding="GB18030"%>  
<%@ taglib prefix="s" uri="/struts-tags" %>  
<%  
String path = request.getContextPath();  
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
%>  
  
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
<html>  
  <head>  
    <base href="<%=basePath%>">  
      
    <title>My JSP 'index.jsp' starting page</title>  
    <meta http-equiv="pragma" content="no-cache">  
    <meta http-equiv="cache-control" content="no-cache">  
    <meta http-equiv="expires" content="0">      
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">  
    <meta http-equiv="description" content="This is my page">  
    <!-- 
    <link rel="stylesheet" type="text/css" href="styles.css"> 
    -->  
  </head>  
    
  <body>  
     <s:form action="los" method="post">  
        <s:textfield label="請輸入用戶名" name="username"></s:textfield>  
        <s:password label="請輸入密碼" name="userpass"></s:password>  
        <s:submit/>  
     </s:form>  
  </body>  
</html>  

定義Action類

Action中定義了接受前臺兩個參數的屬性


import com.opensymphony.xwork2.ActionSupport;  
  
public class LoginAction extends ActionSupport {  
    private String username;  
    private String userpass;  
    public String getUsername() {  
        return username;  
    }  
    public void setUsername(String username) {  
        this.username = username;  
    }  
    public String getUserpass() {  
        return userpass;  
    }  
    public void setUserpass(String userpass) {  
        this.userpass = userpass;  
    }  
    public String execute() throws Exception{  
        return SUCCESS;  
    }  
}  


         該攔截器類繼承了AbstractInterceptor類,可以不寫init()和destory()方法,但是必須實現intercept(ActionInvocation invocation)方法

當攔截到內容後,系統首先打印出"攔截器開始工作......"

由於invocation中已經存在Action對象,所以按照我們說的得Action者得天下原則,爲Action中的兩個屬性username ,userpass賦值。


        這裏存在一個很重要的問題:如果不爲Action的兩個元素賦值,那Action類不像我們以前沒有攔截器認識的一樣,Action中的username和userpass將不會在自動取得前臺index.jsp頁面傳送過來的值,即使滿足條件,如果Action類中還需要對username和userpass兩個屬性做進一步處理的話,username和userpass將會是空值。


解決辦法:

        首先必須明白的是在提交後,Struts2將會自動調用LoginAction動作類中的setUsername方法,並將username文本框中的值通過setUserame方法的參數傳入。實際上,這個操作是由params攔截器完成的,params對應的類是com.opensymphony.xwork2.interceptor.ParametersInterceptor。由於params已經在defaultStack中定義,因此,在未引用攔截器的< action>中是會自動引用params的。

但是,如果引用了自定義攔截器,那我們就要現實的在Action中引用params攔截器。


如果滿足用戶名和密碼是user 和123,則調用invocation.invoke()方法,該方法的作用是調用下一個攔截器或Action(如果是最後一個攔截器了,則調用Action);


import javax.servlet.http.HttpServletRequest;  
  
import org.apache.struts2.ServletActionContext;  
  
import com.opensymphony.xwork2.ActionInvocation;  
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;  
import com.zhuxuli.action.LoginAction;  
  
public class LoInterceptor extends AbstractInterceptor{  
    public String intercept(ActionInvocation invocation) throws Exception{  
        System.out.println("攔截器開始工作.....");  
        HttpServletRequest request=ServletActionContext.getRequest();  
        LoginAction action=(LoginAction)invocation.getAction();  
        action.setUsername(request.getParameter("username"));  
        action.setUserpass(request.getParameter("userpass"));  
        if(action.getUsername().equals("user")&&action.getUserpass().equals("123")){  
            String result=invocation.invoke();  
            System.out.println("result="+result);  
            return result;  
        }else{  
            return "input";  
        }  
    }  
}  

然後配置struts.xml文件


<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE struts PUBLIC  
    "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"  
    "http://struts.apache.org/dtds/struts-2.1.7.dtd">  
<struts>  
   <constant name="struts.i18n.encoding" value="gbk" />  
    <constant name="struts.devMode" value="true" />  
   <package name="zxl" extends="struts-default">  
       <interceptors>  
          <interceptor name="a1" class="com.zhuxuli.Iterceptors.LoInterceptor">  
          </interceptor>  
       </interceptors>  
       <action name="los" class="com.yao.action.LoginAction">  
          <result name="input">/index.jsp</result>  
          <result name="success">/success.jsp</result>  
          <interceptor-ref name="a1"></interceptor-ref>  
       </action>  
   </package>  
</struts>  

實例運行後,如果用戶名和密碼不是user和123,頁面返回index.jsp頁面繼續輸入,滿足條件,則返回爲success.jsp頁面。


必須注意的是:

在執行返回success.jsp頁面的時候,你會看到控制檯打印出了相應的語句,也就是下面的代碼執行了


System.out.println("result="+result);
return  result;

        也就是說,通過invocation調用invoke()方法執行Action後,Action執行完畢後,又返回到String result=invocation.invoke()代碼下面繼續執行,直到執行結束,也就說明了攔截器的攔截過程爲:如下圖。


        說明:首先調用攔截器1,滿足要求,則通過Invocation.invoke()方法調用下一個攔截器或Action,因爲這裏有下一個攔截器,所以直接調用攔截器2,如果還是滿足條件,則繼續向下調用,知道返回結果,返回結果後,注意的是,還會繼續回到攔截器2執行未完成的代碼,執行完後,繼續執行攔截器1未完成的代碼,直到最後返回結果。







  • Struct2常用的預定義攔截器

1:params攔截器

這個攔截器是必不可少的,因爲就是由它把請求參數設置到相應的Action的屬性去的,並自動進行類型轉換。


2:staticParams攔截器

       將struts.xml配置文件裏定義的Action參數,設置到對應的Action實例中,Action參數使用<param>標籤,是<action>標籤的子元素。


struts.xml的示例如下:

<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">    
            <param name="account">test</param>    
</action>


        這要求Action中一定要有一個account的屬性,並有相應的getter/setter方法。運行的時候,Action的account屬性在初始化過後,會接到這裏的賦值“test”。

       注意:params攔截器和staticParams攔截器都會爲Action的屬性賦值,如果碰到了都要賦同一個值呢,比如request裏面有account參數,而struts.xml中也有account參數,最終的值是誰?

       其實是Action初始化過後,就會把struts.xml中配置的數據設置到Action實例中相應的屬性上去。然後,把用戶請求的數據設置到Action實例中相應的屬性上去。

       很明顯最後的值是用戶請求中account的數據。


3:prepare攔截器

        在Action執行之前調用Action的prepare()方法,這個方法是用來準備Action執行之前要做的工作。它要求我們的Action必需實現com.opensymphony.xwork2.Preparable接口


4:modelDriven攔截器

       如果Action實現ModelDriven接口,它將getModel()取得的模型對象存入OgnlValueStack中。


5:chain攔截器

       將前一個執行結束的Action屬性設置到當前的Action中。它被用在ResultType爲“chain”所指定的結果的Action中,該結果Action對象會從值棧中獲得前一個Action對應的屬性,它實現Action鏈之間的數據傳遞。


6:execption攔截器

       在拋出異常的時候,這個攔截器起作用。它是我們第五章講的Struts2的錯誤處理機制(<exception-mapping>)的基礎,任何應用都應該引用這個攔截器,而且引用的時候,最好把它放在第一位,讓它能捕獲所有的異常。


7:validation攔截器

      調用驗證框架讀取 *-validation.xml文件,並且應用在這些文件中聲明的校驗。


8:token攔截器

      覈對當前Action請求(request)的有效標識,防止重複提交Action請求 。使用標籤<s:token>可以生成表單令牌,該標籤會在session中設置一個預期的值並且在表單中創建一個隱藏的input字段。Token攔截器會檢查這個令牌,如果不合法,將不會執行action,注意這個攔截器需要手工添加,還需要配置一個invalid.token的result。


9:tokenSession攔截器

      擴展了token攔截器的功能,當提交無效的Action請求標識時,它會跳轉回到第一次成功後的頁面。


10:conversionError攔截器

       用來處理框架進行類型轉化(Type Conversion)時的出錯信息。它將存儲在ActionContext中的類型轉化(Type Conversion)錯誤信息轉化成相應的Action字段的錯誤信息,保存在堆棧中。根據需要,可以將這些錯誤信息在視圖中顯示出來。


11:fileUpload攔截器

      用來處理文件上傳。


12:workflow攔截器

        Action默認的工作流,如果action實現了Validateable接口,那麼interceptor會調用action的validate()方法;如果action實現了ValidationAware接口,那麼interceptor將會檢查action是否包含錯誤信息。如果包含任何錯誤信息,那麼interceptor將會返回input,而不讓action執行。


13:servletConfig攔截器

        這個攔截器提供Action直接對Servlet API的訪問,把Servlet API的對象注入到Action中。包括:ServletRequestAware、ServletResponseAware、ParameterAware、SessionAware、ApplicationAware。


14:timer攔截器

       記錄ActionInvocation餘下部分執行的時間,並做爲日誌信息記錄下來,便於尋找性能瓶頸。


15:logger攔截器

       在日誌信息中輸出要執行的Action信息 ,這樣,在調試的時候,就能很快的定位到這個對應的Action了。












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章