SpringBoot之Interceptor

今天將對SpringBoot中的攔截器interceptor從功能、實現(xiàn)、源碼等方面進行分析。

何為攔截器

攔截器嚣崭,在AOP(Aspect-Oriented Programming)中用于在某個方法或字段被訪問之前,進行攔截懦傍,然后在之前或之后加入某些操作雹舀。攔截是AOP的一種實現(xiàn)策略。

攔截器作用

  1. 日志記錄:記錄請求信息的日志粗俱,以便進行信息監(jiān)控说榆、信息統(tǒng)計、計算PV(Page View)等

  2. 權(quán)限檢查:如登錄檢測寸认,進入處理器檢測檢測是否登錄

  3. 性能監(jiān)控:通過攔截器在進入處理器之前記錄開始時間娱俺,在處理完后記錄結(jié)束時間,從而得到該請求的處理時間废麻。(反向代理荠卷,如apache也可以自動記錄);

  4. 通用行為:讀取cookie得到用戶信息并將用戶對象放入請求烛愧,從而方便后續(xù)流程使用油宜,還有如提取Locale掂碱、Theme信息等,只要是多個處理器都需要的即可使用攔截器實現(xiàn)慎冤。

攔截器實現(xiàn)

通過實現(xiàn)HandlerInterceptor接口疼燥,并重寫該接口的三個方法來實現(xiàn)攔截器的自定義。

接口 接口名稱 說明
preHandle 前置處理 在實際的Handle執(zhí)行前執(zhí)行蚁堤;有Boolean類型的返回值醉者,如果返回為False,則Handle本身及postHandle/afterCompletion以及后續(xù)的攔截器全部都不會再繼續(xù)執(zhí)行披诗;為True則反之撬即。
postHandle 后置處理 Handle執(zhí)行后視圖渲染前執(zhí)行
afterCompletion 完成后處理 Handle執(zhí)行且視圖渲染完成后執(zhí)行

運行流程如下:

  1. 攔截器執(zhí)行順序是按照Spring配置文件中定義的順序而定的。
  2. 會先按照順序執(zhí)行所有攔截器的preHandle方法呈队,一直遇到return false為止剥槐,比如第二個preHandle方法是return false,則第三個以及以后所有攔截器都不會執(zhí)行宪摧。若都是return true粒竖,則按順序加載完preHandle方法。
  3. 然后執(zhí)行主方法(自己的controller接口)几于,若中間拋出異常蕊苗,則跟return false效果一致,不會繼續(xù)執(zhí)行postHandle沿彭,只會倒序執(zhí)行afterCompletion方法岁歉。
  4. 在主方法執(zhí)行完業(yè)務(wù)邏輯(頁面還未渲染數(shù)據(jù))時,按倒序執(zhí)行postHandle方法膝蜈。若第三個攔截器的preHandle方法return false,則會執(zhí)行第二個和第一個的postHandle方法和afterCompletion(postHandle都執(zhí)行完才會執(zhí)行這個熔掺,也就是頁面渲染完數(shù)據(jù)后饱搏,執(zhí)行after進行清理工作)方法。(postHandle和afterCompletion都是倒序執(zhí)行)


    攔截器運行流程

一般實現(xiàn)攔截器都是繼承HandlerInterceptorAdapter這個類置逻。這個類是實現(xiàn)AsyncHandlerInterceptor接口的抽象類推沸,而AsyncHandlerInterceptor又是繼承HandlerInterceptor的接口,額外提供了afterConcurrentHandlingStarted方法券坞,該方法是用來處理異步請求的鬓催。這個方法會在Controller方法異步執(zhí)行時開始執(zhí)行,而Interceptor的postHandle方法則是需要等到Controller的異步執(zhí)行完才能執(zhí)行恨锚。異步請求先支持preHandle宇驾、然后執(zhí)行afterConcurrentHandlingStarted。異步線程完成之后執(zhí)行preHandle猴伶、postHandle课舍、afterCompletion塌西。

這里來看一下HandlerInterceptorAdapter的源碼:

 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.handler;

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

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

/**
 * Abstract adapter class for the {@link AsyncHandlerInterceptor} interface,
 * for simplified implementation of pre-only/post-only interceptors.
 *
 * @author Juergen Hoeller
 * @since 05.12.2003
 */
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {

    /**
     * This implementation always returns {@code 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 {
    }

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

}

可見HandlerInterceptorAdapter它“empty”地實現(xiàn)了三個HandlerInterceptor的三個方法和AsyncHandlerInterceptor新增的處理異步方法。如果實現(xiàn)HandlerInterceptor接口的話筝尾,三個方法必須實現(xiàn)捡需,不管需不需要,而HandlerInterceptorAdapter適配器筹淫,允許我們只實現(xiàn)需要的回調(diào)方法站辉,這應(yīng)該算是適配器設(shè)計模式的實現(xiàn)。

下面簡單創(chuàng)建一個自定義的攔截器demo损姜,繼承HandlerInterceptorAdapter饰剥。


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("================================== preHandle1 ===========================================");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("================================== postHandle1 ===========================================");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("================================== afterCompletion1 ===========================================");
    }

}

還需要創(chuàng)建一個配置類將該攔截器注入到spring容器中,這個配置類要實現(xiàn)WebMvcConfigurer薛匪。
(注意:繼承WebMvcConfigurerAdapter類這種方式官方建議廢棄)

public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor());
    }
}

HandlerInterceptor源碼

直接上翻譯后的源碼:

 
    
     /** 
     * preHandle方法是進行處理器攔截用的捐川,顧名思義,該方法將在Controller處理之前進行調(diào)用逸尖,SpringMVC中的Interceptor攔截器是鏈式的古沥,可以同時存在 
     * 多個Interceptor,然后SpringMVC會根據(jù)聲明的前后順序一個接一個的執(zhí)行娇跟,而且所有的Interceptor中的preHandle方法都會在 
     * Controller方法調(diào)用之前調(diào)用岩齿。SpringMVC的這種Interceptor鏈式結(jié)構(gòu)也是可以進行中斷的,這種中斷方式是令preHandle的返 
     * 回值為false苞俘,當preHandle的返回值為false的時候整個請求就結(jié)束了盹沈。 
     */  
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception;
 
    
    /** 
     * 這個方法只會在當前這個Interceptor的preHandle方法返回值為true的時候才會執(zhí)行。postHandle是進行處理器攔截用的吃谣,它的執(zhí)行時間是在處理器進行處理之 
     * 后乞封,也就是在Controller的方法調(diào)用之后執(zhí)行,但是它會在DispatcherServlet進行視圖的渲染之前執(zhí)行岗憋,也就是說在這個方法中你可以對ModelAndView進行操 
     * 作肃晚。這個方法的鏈式結(jié)構(gòu)跟正常訪問的方向是相反的,也就是說先聲明的Interceptor攔截器該方法反而會后調(diào)用仔戈,這跟Struts2里面的攔截器的執(zhí)行過程有點像关串, 
     * 只是Struts2里面的intercept方法中要手動的調(diào)用ActionInvocation的invoke方法,Struts2中調(diào)用ActionInvocation的invoke方法就是調(diào)用下一個Interceptor 
     * 或者是調(diào)用action监徘,然后要在Interceptor之前調(diào)用的內(nèi)容都寫在調(diào)用invoke之前晋修,要在Interceptor之后調(diào)用的內(nèi)容都寫在調(diào)用invoke方法之后。 
     */
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;
 
    
    /** 
     * 該方法也是需要當前對應(yīng)的Interceptor的preHandle方法的返回值為true時才會執(zhí)行凰盔。該方法將在整個請求完成之后墓卦,也就是DispatcherServlet渲染了視圖執(zhí)行, 
     * 這個方法的主要作用是用于清理資源的户敬,當然這個方法也只能在當前這個Interceptor的preHandle方法的返回值為true時才會執(zhí)行趴拧。 
     */ 
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;
 
}

每一個攔截器都有一個preHandle()方法和postHandle()方法溅漾。在調(diào)用處理器之前后,都會調(diào)用攔截器著榴。由許多個攔截器組成一個執(zhí)行鏈添履,而這個執(zhí)行鏈是以數(shù)組的形式存在。所以脑又,執(zhí)行這個執(zhí)行鏈的中的所有攔截器的preHandle()方法是按照interceptor[]數(shù)組的正序執(zhí)行的暮胧,也就是按數(shù)組下標從小到大執(zhí)行每一個攔截器的preHandle()方法。而攔截器的postHandle()方法的執(zhí)行是按照逆序執(zhí)行的问麸,也就是按照interceptor[]數(shù)組的下標從大到小的順序執(zhí)行攔截器中的postHandle()方法往衷。而攔截器中的每一個afterCompletion()方法都是在渲染完視圖以后按照interceptor[]數(shù)組的下標從大到小的順序執(zhí)行攔截器中的afterCompletion()方法,也就是說afterCompletion()方法的執(zhí)行順序跟postHandle()方法的執(zhí)行順序是一樣的严卖。如下圖:

當然這是正常情況下的執(zhí)行順序席舍,但是當某一個攔截器的preHandle()方法()返回false,那么handler(處理器就不會執(zhí)行)。它會直接去掉用當前攔截器的前一個攔截器的afterCompletion()方法哮笆,從這個方法開始逆序執(zhí)行来颤。比如:這里的interceptor3的preHandle()方法返回false。那么就會直接調(diào)用interceptor2的afterCompletion()方法稠肘,并從這個方法開始逆序執(zhí)行至結(jié)束福铅。

HandlerInterceptor實現(xiàn)原理

下面結(jié)合SpringMVC來看一下攔截器的內(nèi)部實現(xiàn)。源碼上關(guān)鍵地方均加了注釋项阴。

首先滑黔,我們看一下 org.springframework.web.servlet.DispatcherServlet 中doDispatch方法,在這里可見執(zhí)行順序:preHandle->controller對應(yīng)的方法->postHandle->afterCompletion->afterConcurrentHandlingStarted环揽。

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

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                //調(diào)用已注冊HandlerInterceptor的preHandle()方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 真正執(zhí)行Controller對應(yīng)的方法
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                //調(diào)用已注冊HandlerInterceptor的postHandle()方法
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            //調(diào)用已注冊HandlerInterceptor的afterCompletion()方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            //調(diào)用已注冊HandlerInterceptor的afterCompletion()方法
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    //調(diào)用已注冊HandlerInterceptor的afterConcurrentHandlingStarted()方法
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }
    /**
     * Handle the result of handler selection and handler invocation, which is
     * either a ModelAndView or an Exception to be resolved to a ModelAndView.
     */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

        boolean errorView = false;

        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }

        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                        "': assuming HandlerAdapter completed request handling");
            }
        }

        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }

        if (mappedHandler != null) {
            //調(diào)用已注冊HandlerInterceptor的afterCompletion()方法
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

接下來看看 HandlerExecutionChain的applyPreHandle方法實現(xiàn):

    /**
     * 執(zhí)行注冊到該請求上的所有HandlerInterceptor的 preHandle 方法
     */
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }

    public HandlerInterceptor[] getInterceptors() {
        if (this.interceptors == null && this.interceptorList != null) {
            this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
        }
        return this.interceptors;
    }

HandlerExecutionChain的applyPostHandle方法實現(xiàn):

     * 執(zhí)行注冊到該請求上的所有HandlerInterceptor的 postHandle 方法
     */
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }

HandlerExecutionChain的triggerAfterCompletion方法實現(xiàn):

     * 執(zhí)行注冊到該請求上的所有HandlerInterceptor的 afterCompletion 方法
     */
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
            throws Exception {

        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = this.interceptorIndex; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                }
                catch (Throwable ex2) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                }
            }
        }
    }

最后略荡,HandlerExecutionChain的applyAfterConcurrentHandlingStarted方法實現(xiàn):

    /**
     * 執(zhí)行注冊到該請求上的所有AsyncHandlerInterceptor的 afterConcurrentHandlingStarted 方法
     */
    void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                if (interceptors[i] instanceof AsyncHandlerInterceptor) {
                    try {
                        AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
                        asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
                    }
                    catch (Throwable ex) {
                        logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
                    }
                }
            }
        }
    }

接下來,HandlerExecutionChain 內(nèi)部的Interceptor數(shù)組是在什么時候初始化的呢歉胶?

來看看 HandlerExecutionChain 類的定義汛兜,源碼如下:

package org.springframework.web.servlet;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

public class HandlerExecutionChain {

    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

    private final Object handler;

    private HandlerInterceptor[] interceptors;

    private List<HandlerInterceptor> interceptorList;

    private int interceptorIndex = -1;

    public HandlerExecutionChain(Object handler) {
        this(handler, (HandlerInterceptor[]) null);
    }

    public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
        if (handler instanceof HandlerExecutionChain) {
            HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
            this.handler = originalChain.getHandler();
            this.interceptorList = new ArrayList<HandlerInterceptor>();
            CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
            CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
        }
        else {
            this.handler = handler;
            this.interceptors = interceptors;
        }
    }
    
    /**
     * 向handler中添加interceptor
     */
    public void addInterceptor(HandlerInterceptor interceptor) {
        initInterceptorList().add(interceptor);
    }

    /**
     * 向handler中添加多個interceptor
     */
    public void addInterceptors(HandlerInterceptor... interceptors) {
        if (!ObjectUtils.isEmpty(interceptors)) {
            initInterceptorList().addAll(Arrays.asList(interceptors));
        }
    }

    private List<HandlerInterceptor> initInterceptorList() {
        if (this.interceptorList == null) {
            this.interceptorList = new ArrayList<HandlerInterceptor>();
            if (this.interceptors != null) {
                // An interceptor array specified through the constructor
                this.interceptorList.addAll(Arrays.asList(this.interceptors));
            }
        }
        this.interceptors = null;
        return this.interceptorList;
    }
    
    /**
     * 按順序返回handler上的HandlerInterceptor列表
     */
    public HandlerInterceptor[] getInterceptors() {
        if (this.interceptors == null && this.interceptorList != null) {
            this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
        }
        return this.interceptors;
    }
}

HandlerExecutionChain 提供了addInterceptors()方法來添加HandlerInterceptor,那addInterceptors是在哪里被調(diào)用的呢跨扮?

回到DispatcherServlet 的 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 {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // 獲取當前請求對應(yīng)的handler
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                ...
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                ...
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            ...
        }
    }
    
    /**
     * 獲取當前請求對應(yīng)的HandlerExecutionChain
     */
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

接著來看看HandlerMapping 的getHandler(request)方法验毡,HandlerMapping是一個接口衡创,getHandler(request)方法是在AbstractHandlerMapping中實現(xiàn)的,如下:

    @Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //獲取請求對應(yīng)的handler
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }
        //根據(jù)handler構(gòu)造HandlerExecutionChain
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }
    
    /**
     * 構(gòu)造HandlerExecutionChain, 并初始化HandlerInterceptor
     */
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
            else {
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }

可見晶通,在這里調(diào)用了addInterceptor璃氢,構(gòu)造HandlerExecutionChain并初始化HandlerInterceptor。

該方法就是從adaptedInterceptors屬性中狮辽,根據(jù)URL查找添加條件的Interceptor并組裝成HandlerExecutionChain并返回一也。
此處是否滿足條件的判斷是根據(jù)添加攔截器配置時調(diào)用的addPathPatterns方法決定的巢寡。

那么,要是還想繼續(xù)深究下去的話:

現(xiàn)在的問題就是adaptedInterceptors屬性是如何初始化的椰苟。
通過分析AbstractHandlerMapping類抑月,其adaptedInterceptors屬性實際是在initInterceptors方法中根據(jù)interceptors來進行初始化的。現(xiàn)在的問題轉(zhuǎn)變成interceptors這個屬性是如何初始化的了舆蝴。 實際上這個屬性是通過setInterceptors方法來設(shè)置的谦絮,但通過Alt+F7的搜索并未搜索到該方法是在哪個地方調(diào)用的。

我們換個思路洁仗,通過@EnableWebMvc來分析看通過addInterceptors方法配置的Interceptor在到底添加到哪去了层皱。
前言已經(jīng)分析,通過@EnableWebMvc注解實際上引入了DelegatingWebMvcConfiguration這個類赠潦;查看這個類叫胖,在其中有一方法被Autowired注解:

public void setConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
        this.configurers.addWebMvcConfigurers(configurers);
    }
}

通過查看Autowired注解定義,了解到當它使用在List參數(shù)的方法上時她奥,會查找List所包含的對象類型的所有Bean然后進行注入瓮增。這也意味著,此處會將所有實現(xiàn)WebMvcConfigurer接口的類進行注入方淤,然后添加到configurers屬性中去钉赁;在此處,我們自定義的繼承自WebMvcConfigurer的類會被注入携茂。
再查看 DelegatingWebMvcConfiguration 這個類你踩,它繼承了 WebMvcConfigurationSupport 類。分析WebMvcConfigurationSupport讳苦,可以看到以下方法:

public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    mapping.setOrder(0);
    mapping.setInterceptors(getInterceptors());
    mapping.setContentNegotiationManager(mvcContentNegotiationManager());
    mapping.setCorsConfigurations(getCorsConfigurations());

    PathMatchConfigurer configurer = getPathMatchConfigurer();
    Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
    Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
    Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
    if (useSuffixPatternMatch != null) {
        mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
    }
    if (useRegisteredSuffixPatternMatch != null) {
        mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
    }
    if (useTrailingSlashMatch != null) {
        mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
    }

    UrlPathHelper pathHelper = configurer.getUrlPathHelper();
    if (pathHelper != null) {
        mapping.setUrlPathHelper(pathHelper);
    }

    PathMatcher pathMatcher = configurer.getPathMatcher();
    if (pathMatcher != null) {
        mapping.setPathMatcher(pathMatcher);
    }

    return mapping;
}

可以看到RequestMappingHandlerMapping類被注入Spring容器带膜。
同時通過mapping.setInterceptors(getInterceptors())將所有的Interceptors設(shè)置到HandperMapping對象中 。
這樣就找到了ReuqestMappingHandlerMapping的setInterceptors方法調(diào)用處了鸳谜。
接下來的問題就是此處調(diào)用的getInterceptors方法的實現(xiàn):

    if (this.interceptors == null) {
        InterceptorRegistry registry = new InterceptorRegistry();
        addInterceptors(registry);
        registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
        registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
        this.interceptors = registry.getInterceptors();
    }
    return this.interceptors.toArray();
}

此處如果interceptors對象為空時膝藕,會調(diào)用addInterceptors方法;其實現(xiàn)在DelegatingWebMvcConfiguration類中:

protected void addInterceptors(InterceptorRegistry registry) {
    this.configurers.addInterceptors(registry);
}

在前文已經(jīng)描述到咐扭,DelegatingWebMvcConfiguration類中的configurers屬性會將所有繼承了WebMvcConfigurer的配置類全部添加進去芭挽。如我們自定義的配置類;在此處調(diào)用DelegatingWebMvcConfiguration的addInterceptors方法時蝗肪,實際就是調(diào)用各個WebMvcConfigurer對象的addInterceptors方法來完成自定義的Interceptor注冊過程袜爪。
通過這一系列過程,RequestMappingHandlerMapping的getInterceptors方法就可以獲取到所有自定義的Interceptor了薛闪。

完事辛馆!

注:

實現(xiàn)原理部分參考結(jié)合了http://www.reibang.com/p/b849f9919130https://blog.csdn.net/icarusliu/article/details/78833520,兩位作者正好各分析了上半段和下半段的源碼豁延。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昙篙,一起剝皮案震驚了整個濱河市腊状,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌苔可,老刑警劉巖缴挖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異硕蛹,居然都是意外死亡醇疼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門法焰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秧荆,“玉大人,你說我怎么就攤上這事埃仪∫冶簦” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵卵蛉,是天一觀的道長颁股。 經(jīng)常有香客問我,道長傻丝,這世上最難降的妖魔是什么甘有? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捞附。我一直安慰自己,他們只是感情好滤愕,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怜校,像睡著了一般间影。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茄茁,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天魂贬,我揣著相機與錄音,去河邊找鬼裙顽。 笑死付燥,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的锦庸。 我是一名探鬼主播机蔗,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蒲祈,長吁一口氣:“原來是場噩夢啊……” “哼甘萧!你這毒婦竟也來了萝嘁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤扬卷,失蹤者是張志新(化名)和其女友劉穎牙言,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怪得,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡咱枉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了徒恋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚕断。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖入挣,靈堂內(nèi)的尸體忽然破棺而出亿乳,到底是詐尸還是另有隱情,我是刑警寧澤径筏,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布葛假,位于F島的核電站,受9級特大地震影響滋恬,放射性物質(zhì)發(fā)生泄漏聊训。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一恢氯、第九天 我趴在偏房一處隱蔽的房頂上張望带斑。 院中可真熱鬧,春花似錦酿雪、人聲如沸遏暴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朋凉。三九已至,卻和暖如春醋安,著一層夾襖步出監(jiān)牢的瞬間杂彭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工吓揪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留亲怠,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓柠辞,卻偏偏與公主長得像团秽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 姓名: 李小娜 [嵌牛導(dǎo)讀]: SpringMVC 中的Interceptor 攔截器也是相當重要和相當有用的习勤,...
    n184閱讀 3,162評論 0 4
  • 本文作者:鐘昕靈踪栋,叩丁狼高級講師。原創(chuàng)文章图毕,轉(zhuǎn)載請注明出處夷都。 前言 Spring MVC屬于SpringFrame...
    叩丁狼教育閱讀 3,296評論 1 4
  • 1、Spring MVC請求流程 (1)初始化:(對DispatcherServlet和ContextLoderL...
    拾壹北閱讀 1,948評論 0 12
  • 在使用SpringMVC攔截器的時候予颤,我們接觸的最多的便是HandlerInterceptor接口囤官,因為我們所有的...
    谷和阿秋閱讀 8,503評論 0 3
  • 世上有一樣?xùn)|西,比任何別的東西都更忠誠于你蛤虐,那就是你的經(jīng)歷党饮。你生命中的日子,你在其中遭遇的人和事驳庭,你因這些遭遇產(chǎn)生...
    劉鼻涕26閱讀 145評論 0 0