天天看點

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

目錄

一、原因

二、自定義攔截器

三、源碼解析

doDispatch()方法

applyPreHandle()方法

postHandle()方法

afterCompletion()方法

攔截器鍊的由來

getHandler(processedRequest)方法

mapping.getHandler(request)方法

getHandlerExecutionChain(handler, request)

chain.addInterceptor(interceptor)

interceptors的值

一、原因

在之前,總是對過濾器、攔截器、監聽器、MethodInterceptor(AOP)總是懵懵懂懂,不明白什麼時候用過濾器,什麼時候用攔截器好,隻是工作中需要了就去百度,然後對比半天,找個看着差不多的把他寫的代碼粘過來放到自己的項目裡再調試。

但是沒有去思考它是怎麼實作的,是誰調用的,為什麼我這麼寫就可以調到?今天就來詳細的介紹一下。

二、自定義攔截器

首先需要知道的是HandlerInterceptor是屬于SpringMVC的。

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

springboot中攔截器的注冊還是比較友善的。大概下面兩個步驟:

第一步,建立攔截器DemoInterceptor類:

可以實作接口HandlerInterceptor,也可以繼承HandlerInterceptorAdapter類,兩種方法一樣。

package com.demo.aop;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class DemoInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("DemoInterceptor:" + request.getRequestURL().toString());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }

}
           

第二步,配置攔截器:

package com.demo.aop;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 配置攔截器
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new DemoInterceptor())
            .addPathPatterns("/**")
            .excludePathPatterns("/static/login.html");
    }
    
    
}
           

至此我們就自定義了自己的攔截器,在HandlerInterceptor中有三個方法,我們可以在自定義的攔截器中重寫這三個方法來實作自己的業務邏輯,從下圖可以看出這三個方法的執行順序。

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

注意點:攔截器中方法的執行順序是 preHandle -> Controller -> postHandle -> afterCompletion,隻有preHandle傳回true,才會執行後面的方法

三、源碼解析

為什麼說HandlerInterceptor是屬于SpringMVC,因為這三個方法都是在SpringMVC的DispatchServlet#doDispatch()方法中調用的,對于DispatchServlet類大家應該都非常熟悉了。

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

doDispatch()方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    // 擷取攔截器鍊HandlerExecutionChain
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                    // 1、執行preHandle()方法
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                    // 2、執行controller中的方法
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    // 3、執行postHandle()方法
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
                // 4、調用afterCompletion()方法
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                // 4、内部調用了afterCompletion()方法,就算報異常也會調用afterCompletion()方法
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                // 4、内部調用了afterCompletion()方法,就算報異常也會調用afterCompletion()方法
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }
           

applyPreHandle()方法

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

applyPostHandle()方法

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

afterCompletion()方法

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

攔截器鍊的由來

攔截器實作的核心是攔截器鍊,我們來看一下攔截器鍊是如何形成的,即HandlerExecutionChain類。

在上面的三個方法中,都是通過this.getInterceptors()方法擷取HandlerInterceptor數組,數組又是将攔截器List集合轉化的,那麼攔截器又是什麼時候放入到List集合中的呢?

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

關鍵還在doDispatch()方法中,通過getHandler(processedRequest)擷取HandlerExecutionChain,在這個方法中将自定義攔截器添加到interceptorList中的。

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

getHandler(processedRequest)方法

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

mapping.getHandler(request)方法

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

getHandlerExecutionChain(handler, request)

真正擷取攔截器鍊的方法

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來
攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

chain.addInterceptor(interceptor)

調用chain.addInterceptor(interceptor)方法添加到interceptorList集合中,至此我們就找到了攔截器List集合的來源。

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來
攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

但是我們自定義的攔截器又是什麼時候加載的呢?還記得我之前說springboot初始化會調用initApplicationContext()方法麼?在下圖debug中我們看到了自己定義的DemoInterceptor攔截器,那麼interceptors的值從哪裡來?

interceptors的值

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

在目前類中搜尋,發現是在setInterceptors()方法中添加的,但是參數是傳入的,在此處打斷點一探究竟。

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來
攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

進入到requestMappingHandlerMapping()方法中,發現是在建立這個對象執行個體化時,調用getInterceptors()擷取的自定義攔截器。

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

getInterceptors()方法

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

addInterceptors(registry)執行會調用我們寫的攔截器配置類InterceptorConfig類,在其中配置了我們自己的攔截器。

攔截器 HandlerInterceptor(SpringMVC)自定義及原理分析一、原因二、自定義攔截器三、源碼解析攔截器鍊的由來

至此我們能夠大體了解攔截器的調用邏輯,以及攔截器的由來,調用等原理。