處理器攔截器(HandlerInterceptor)詳解


處理器攔截器(HandlerInterceptor)詳解

96 
編程界的小學生 
2017.04.06 15:19* 字數 881 閱讀 657評論 0

簡介
SpringWebMVC的處理器攔截器,類似於Servlet開發中的過濾器Filter,用於處理器進行預處理和后處理。

應用場景
1、日志記錄,可以記錄請求信息的日志,以便進行信息監控、信息統計等。
2、權限檢查:如登陸檢測,進入處理器檢測是否登陸,如果沒有直接返回到登陸頁面。
3、性能監控:典型的是慢日志。

HandlerInterceptor

public interface HandlerInterceptor {

   /**
     * 預處理回調方法,實現處理器的預處理(如檢查登陸),第三個參數為響應的處理器,自定義Controller
     * 返回值:true表示繼續流程(如調用下一個攔截器或處理器);false表示流程中斷(如登錄檢查失敗),不會繼續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;
   */
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

   /**
     * 后處理回調方法,實現處理器的后處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能為null。
   */
    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

   /**
    * 整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中
   */
    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

}

攔截器適配器HandlerInterceptorAdapter
有時候我們可能只需要實現三個回調方法中的某一個,如果實現HandlerInterceptor接口的話,三個方法必須實現,不管你需不需要,此時spring提供了一個HandlerInterceptorAdapter適配器(種適配器設計模式的實現),允許我們只實現需要的回調方法。

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {

    /**
     * 默認是true
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }

    /**
     * This implementation is empty.
     */
    @Override
    public void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
    }

    /**
     * This implementation is empty.
     */
    @Override
    public void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }

    /**
     * 不是HandlerInterceptor的接口實現,是AsyncHandlerInterceptor的,AsyncHandlerInterceptor實現了HandlerInterceptor
     */
    @Override
    public void afterConcurrentHandlingStarted(
            HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
    }

}

這樣在我們業務中比如要記錄系統日志,日志肯定是在afterCompletion之后記錄的,否則中途失敗了,也記錄了,那就扯淡了。一定是程序正常跑完后,我們記錄下那些對數據庫做個增刪改的操作日志進數據庫。所以我們只需要繼承HandlerInterceptorAdapter,並重寫afterCompletion一個方法即可,因為preHandle默認是true。

運行流程圖

 
Paste_Image.png

運行流程總結如下:

1、攔截器執行順序是按照Spring配置文件中定義的順序而定的。

2、會先按照順序執行所有攔截器的preHandle方法,一直遇到return false為止,比如第二個preHandle方法是return false,則第三個以及以后所有攔截器都不會執行。若都是return true,則按順序加載完preHandle方法。

3、然后執行主方法(自己的controller接口),若中間拋出異常,則跟return false效果一致,不會繼續執行postHandle,只會倒序執行afterCompletion方法。

4、在主方法執行完業務邏輯(頁面還未渲染數據)時,按倒序執行postHandle方法。若第三個攔截器的preHandle方法return false,則會執行第二個和第一個的postHandle方法和afterCompletion(postHandle都執行完才會執行這個,也就是頁面渲染完數據后,執行after進行清理工作)方法。(postHandle和afterCompletion都是倒序執行)

正常流程Demo

package com.bshf.recipe.demo.syslog;


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

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

public class SyslogInterceptor implements HandlerInterceptor {

    /**
     * 預處理回調方法,實現處理器的預處理(如檢查登陸),第三個參數為響應的處理器,自定義Controller
     * 返回值:true表示繼續流程(如調用下一個攔截器或處理器);false表示流程中斷(如登錄檢查失敗),不會繼續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("==========================================1================================================");
        return true;
    }

    /**
     * 后處理回調方法,實現處理器的后處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能為null。
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("==========================================post1================================================");
    }

    /**
     * 整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("==========================================after1================================================");
    }
    
}
package com.bshf.recipe.demo.syslog;

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

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

public class SyslogInterceptor2 implements HandlerInterceptor {

    /**
     * 預處理回調方法,實現處理器的預處理(如檢查登陸),第三個參數為響應的處理器,自定義Controller
     * 返回值:true表示繼續流程(如調用下一個攔截器或處理器);false表示流程中斷(如登錄檢查失敗),不會繼續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("==========================================2================================================");
        return true;
    }


    /**
     * 后處理回調方法,實現處理器的后處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能為null。
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("==========================================post2================================================");
    }

    /**
     * 整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("==========================================after2================================================");
    }
}
package com.bshf.recipe.demo.syslog;

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

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

public class SyslogInterceptor3 implements HandlerInterceptor {

    /**
     * 預處理回調方法,實現處理器的預處理(如檢查登陸),第三個參數為響應的處理器,自定義Controller
     * 返回值:true表示繼續流程(如調用下一個攔截器或處理器);false表示流程中斷(如登錄檢查失敗),不會繼續調用其他的攔截器或處理器,此時我們需要通過response來產生響應;
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("==========================================3================================================");
        return true;
    }


    /**
     * 后處理回調方法,實現處理器的后處理(但在渲染視圖之前),此時我們可以通過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能為null。
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("==========================================post3================================================");
    }

    /**
     * 整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中我們可以在此記錄結束時間並輸出消耗時間,還可以進行一些資源清理,類似於try-catch-finally中的finally,但僅調用處理器執行鏈中
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("==========================================after3================================================");
    }
}
<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.bshf.recipe.demo.syslog.SyslogInterceptor" />
        </mvc:interceptor>
        
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.bshf.recipe.demo.syslog.SyslogInterceptor2" />
        </mvc:interceptor>
        
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.bshf.recipe.demo.syslog.SyslogInterceptor3" />
        </mvc:interceptor>
</mvc:interceptors>

<mvc:mapping path="/**"/>需要攔截的路徑,這里匹配所有。這里的配置順序將是攔截器執行順序。

輸出結果:

==========================================1================================================
==========================================2================================================
==========================================3================================================
==========================================post3================================================
==========================================post2================================================
==========================================post1================================================
==========================================after3================================================
==========================================after2================================================
==========================================after1================================================

post和after是倒序的!!!!

中斷流程Demo
將SyslogInterceptor3的preHandle方法返回false。其他不變,再看輸出結果

==========================================1================================================
==========================================2================================================
==========================================3================================================
==========================================after2================================================
==========================================after1================================================

發現沒有了post,post方法一個都沒有輸出。不僅如此,你的主程序代碼(xxx.controller的一個接口)都不會被執行,所以只要遇到return false,則直接進行after清理工作,post和自己主程序代碼都將被略過。

 

 
 
來源:http://www.jianshu.com/p/1e8d088c2be9


免責聲明!

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



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