淺談Struts2攔截器的原理與實現


攔截器與過濾器     

     攔截器是對調用的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>
login.jsp  登陸頁面
  <!-- 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>
web.xml  web配置文件
<?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>
struts.xml  struts2配置文件
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;
    }
    
}
Users.java  用戶實體類
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;
    }

}
TimeConsumingInterceptor.java  攔截器
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();
    }


}
CheckLoginInterceptor.java  攔截器
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();
    }

}
OtherInterceptor.java  攔截器
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;
    }
    
}
UsersAction.java  控制器
  <body>
        登錄成功進入首頁。。
  </body>
home.jsp  登錄成功頁面
  <body>
        登陸失敗頁面。。
  </body>
file.jsp  登錄失敗頁面

全局攔截器:控制台輸出登錄成功的攔截器執行順序。

 

局部攔截器:控制台輸出登錄成功的攔截器執行順序。當定義局部攔截器時,則不會再走全局攔截器。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM