SpringMVC攔截器的使用


SpringMVC 中的Interceptor 攔截器也是相當重要和相當有用的,它的主要作用是攔截用戶的請求並進行相應的處理。比如通過它來進行權限驗證,或者是來判斷用戶是否登陸,或者是像12306 那樣子判斷當前時間是否是購票時間。

一、springMVC攔截器的實現方式

springMVC攔截器的實現一般有兩種方式:
第一種方式是要定義的Interceptor類要實現了Spring的HandlerInterceptor 接口。
第二種方式是繼承實現了HandlerInterceptor接口的類,比如Spring已經提供的實現了HandlerInterceptor接口的抽象類HandlerInterceptorAdapter。

1.實現HandlerInterceptor 接口

HandlerInterceptor 接口中定義了三個方法,我們就是通過這三個方法來對用戶的請求進行攔截處理的。

(1)preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顧名思義,該方法將在請求處理之前進行調用。SpringMVC 中的Interceptor 是鏈式的調用的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值Boolean 類型的,當它返回爲false 時,表示請求結束,後續的Interceptor 和Controller 都不會再執行;當返回值爲true 時就會繼續調用下一個Interceptor 的preHandle 方法,如果已經是最後一個Interceptor 的時候就會是調用當前請求的Controller 方法。

 (2)postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解釋我們知道這個方法包括後面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值爲true 時才能被調用。postHandle 方法,顧名思義就是在當前請求進行處理之後,也就是Controller 方法調用之後執行,但是它會在DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對Controller 處理之後的ModelAndView 對象進行操作。postHandle 方法被調用的方向跟preHandle 是相反的,也就是說先聲明的Interceptor 的postHandle 方法反而會後執行,這和Struts2 裏面的Interceptor 的執行過程有點類型。Struts2 裏面的Interceptor 的執行過程也是鏈式的,只是在Struts2 裏面需要手動調用ActionInvocation 的invoke 方法來觸發對下一個Interceptor 或者是Action 的調用,然後每一個Interceptor 中在invoke 方法調用之前的內容都是按照聲明順序執行的,而invoke 方法之後的內容就是反向的。

(3)afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值爲true 時纔會執行。顧名思義,該方法將在整個請求結束之後,也就是在DispatcherServlet 渲染了對應的視圖之後執行。這個方法的主要作用是用於進行資源清理工作的。

多個攔截器的調用順序:

下面是一個簡單的代碼說明:

複製代碼

複製代碼

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class SpringMVCInterceptor implements HandlerInterceptor {


    /**
     * preHandle方法是進行處理器攔截用的,顧名思義,該方法將在Controller處理之前進行調用,SpringMVC中的Interceptor攔截器是鏈式的,可以同時存在
      * 多個Interceptor,然後SpringMVC會根據聲明的前後順序一個接一個的執行,而且所有的Interceptor中的preHandle方法都會在
      * Controller方法調用之前調用。SpringMVC的這種Interceptor鏈式結構也是可以進行中斷的,這種中斷方式是令preHandle的返
      * 回值爲false,當preHandle的返回值爲false的時候整個請求就結束了。
      */
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        // TODO Auto-generated method stub
        return false;
    }
    
    /**
     * 這個方法只會在當前這個Interceptor的preHandle方法返回值爲true的時候纔會執行。postHandle是進行處理器攔截用的,它的執行時間是在處理器進行處理之
      * 後,也就是在Controller的方法調用之後執行,但是它會在DispatcherServlet進行視圖的渲染之前執行,也就是說在這個方法中你可以對ModelAndView進行操
      * 作。這個方法的鏈式結構跟正常訪問的方向是相反的,也就是說先聲明的Interceptor攔截器該方法反而會後調用,這跟Struts2裏面的攔截器的執行過程有點像,
      * 只是Struts2裏面的intercept方法中要手動的調用ActionInvocation的invoke方法,Struts2中調用ActionInvocation的invoke方法就是調用下一個Interceptor
     * 或者是調用action,然後要在Interceptor之前調用的內容都寫在調用invoke之前,要在Interceptor之後調用的內容都寫在調用invoke方法之後。
      */
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        
    }

    /**
     * 該方法也是需要當前對應的Interceptor的preHandle方法的返回值爲true時纔會執行。該方法將在整個請求完成之後,也就是DispatcherServlet渲染了視圖執行,
      * 這個方法的主要作用是用於清理資源的,當然這個方法也只能在當前這個Interceptor的preHandle方法的返回值爲true時纔會執行。
      */
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
    throws Exception {
        // TODO Auto-generated method stub
        
    }
    
}

複製代碼

複製代碼

2.繼承HandlerInterceptorAdapter類

有時候我們可能只需要實現三個回調方法中的某一個,如果實現HandlerInterceptor接口的話,三個方法必須實現,不管你需不需要,此時spring提供了一個HandlerInterceptorAdapter適配器(一種適配器設計模式的實現),允許我們只實現需要的回調方法(注:一般項目中使用比較多的是這種方式)。

HandlerInterceptorAdapter適配器是Spring MVC爲了方便我們使用HandlerInterceptor而對HandlerInterceptor 的默認實現,裏面的3個方法沒有做任何處理,在preHandle方法直接返回true,這樣我們繼承HandlerInterceptorAdapter後只需要實現3個方法中我們需要的方法即可,而不像繼承HandlerInterceptor一樣不管是否需要3個方法都要實現。

複製代碼

複製代碼

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class CommonInterceptor  extends HandlerInterceptorAdapter{
    
    private final Logger log = LoggerFactory.getLogger(CommonInterceptor.class);

    /** 
     * 在業務處理器處理請求之前被調用 
      * 如果返回false,從當前的攔截器往回執行所有攔截器的afterCompletion(),再退出攔截器鏈。
      * 如果返回true,執行下一個攔截器,直到所有的攔截器都執行完畢,再執行被攔截的Controller。
      * 然後進入攔截器鏈,從最後一個攔截器往回執行所有的postHandle() 
     * 接着再從最後一個攔截器往回執行所有的afterCompletion() 
     */  
    @Override  
    public boolean preHandle(HttpServletRequest request,  
            HttpServletResponse response, Object handler) throws Exception {
            
        log.info("==============執行順序: 1、preHandle================");          
    }
    
    /**
     * 在業務處理器處理請求執行完成後,生成視圖之前執行的動作   
      * 可在modelAndView中加入數據,比如當前時間
      */
    @Override  
    public void postHandle(HttpServletRequest request,  
            HttpServletResponse response, Object handler,  
            ModelAndView modelAndView) throws Exception {
            
        log.info("==============執行順序: 2、postHandle================");  
        if(modelAndView != null){  //加入當前時間  
            modelAndView.addObject("haha", "測試postHandle");  
        }  

    }
    
    /** 
     * 在DispatcherServlet完全處理完請求後被調用,可用於清理資源等    
      * 當有攔截器拋出異常時,會從當前攔截器往回執行所有的攔截器的afterCompletion() 
     */  
    @Override  
    public void afterCompletion(HttpServletRequest request,  
            HttpServletResponse response, Object handler, Exception ex)  
            throws Exception {  
        
        log.info("==============執行順序: 3、afterCompletion================");  

    }  
}

複製代碼

複製代碼

二、把定義的攔截器類加入到SpringMVC的攔截體系中

1.在SpringMVC的配置文件中加上支持MVC的schema

xmlns:mvc="http://www.springframework.org/schema/mvc"  
xsi:schemaLocation=" http://www.springframework.org/schema/mvc   
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"

2.使用mvc:interceptors標籤來聲明需要加入到SpringMVC攔截器鏈中的攔截器

複製代碼

複製代碼

<!--配置攔截器, 多個攔截器,順序執行 -->
    <mvc:interceptors> 
    <!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根下面的Interceptor將攔截所有的請求 -->  
        <mvc:interceptor>
            <mvc:mapping path="/test/number.do"/> 
            <!-- 定義在mvc:interceptor下面的表示是對特定的請求才進行攔截的 -->  
            <bean id="commonInterceptor" class="org.shop.interceptor.CommonInterceptor"></bean>
        </mvc:interceptor> 
    <!-- 當設置多個攔截器時,先按順序調用preHandle方法,然後逆序調用每個攔截器的postHandle和afterCompletion方法  -->
</mvc:interceptors>

複製代碼

複製代碼

由上面的示例可以看出可以利用mvc:interceptors標籤聲明一系列的攔截器,然後它們就可以形成一個攔截器鏈,攔截器的執行順序是按聲明的先後順序執行的,先聲明的攔截器中的preHandle方法會先執行,然而它的postHandle方法和afterCompletion方法卻會後執行。

 在mvc:interceptors標籤下聲明interceptor主要有兩種方式:

(1)直接定義一個Interceptor實現類的bean對象。使用這種方式聲明的Interceptor攔截器將會對所有的請求進行攔截。

(2)使用mvc:interceptor標籤進行聲明。使用這種方式進行聲明的Interceptor可以通過mvc:mapping子標籤來定義需要進行攔截的請求路徑。

經過上述兩步之後,定義的攔截器就會發生作用對特定的請求進行攔截了。

注:使用mvc:exclude-mapping 可以排除攔截的地址

複製代碼

複製代碼

<mvc:interceptors>
    <mvc:interceptor>
        <!--  
        /**的意思是所有文件夾及裏面的子文件夾 
         /*是所有文件夾,不含子文件夾 
         /是web項目的根目錄
         --> 
        <mvc:mapping path="/**" />
        <!-- 需排除攔截的地址 -->
        <mvc:exclude-mapping path="/js/**" />
        <mvc:exclude-mapping path="/css/**" />
        <mvc:exclude-mapping path="/image/**" />
        <!--這個類就是我們自定義的Interceptor -->
        <bean id="commonInterceptor" class="org.shop.interceptor.CommonInterceptor"></bean> 
    </mvc:interceptor>
</mvc:interceptors>

複製代碼

複製代碼


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