Spring MVC 源碼分析之 攔截器 Interceptor

一、前言

在 DispatcherServlet 中的 doDispatch 方法中,我們發(fā)現(xiàn)調(diào)用 getHandler() 方法返回的不是一個純HandlerMethod,而是一個HandlerExecutionChain,這個是為啥呢?因為在執(zhí)行 真正的處理方法前后,會執(zhí)行一下默認攔截器或者自定義攔截器。接下來就慢慢的揭開她的面紗。

二、重溫 doDispatch

首先在回顧一下 doDispatch 方法,請看源碼

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
      //省略代碼....
    try {
      //省略代碼....
        try {
            //省略代碼....

            // Determine handler for the current request.
            //獲取Handler執(zhí)行鏈
            mappedHandler = getHandler(processedRequest);

            //省略代碼....
            //執(zhí)行攔截器方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler.
            // 執(zhí)行真實的處理方法
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
           //省略代碼....
            applyDefaultViewName(processedRequest, mv);

            //執(zhí)行攔截器方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception 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 {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }else {

        }
    }
}
image.gif

主要代碼說明:

  1. mappedHandler = getHandler(processedRequest); 獲取 HandlerExecutionChain 。
  2. mappedHandler.applyPreHandle(processedRequest, response) 執(zhí)行Handler的前置方法。
  3. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 執(zhí)行真正的 處理方法
  4. mappedHandler.applyPostHandle(processedRequest, response, mv); 執(zhí)行攔擊去的后置方法
  5. triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 執(zhí)行攔截器的完成方法

三、攔截器介紹

1、首先查看攔截的接口繼承關系圖

image
image.gif

?

根據(jù)繼承關系圖發(fā)現(xiàn),連接器的頂級接口為 HandlerInterceptor ,不過SpringMVC在也提供了一個適配器類 HandlerInterceptorAdapter,在擴展自己的攔截器時,直接繼承 HandlerInterceptorAdapter 即可,直接重寫,需要關注的方法就行。

2、看看 HandlerInterceptor 都有哪些方法

public interface HandlerInterceptor {

    //前置方法, 獲取到 Handler執(zhí)行真正的 handler之前
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return true;
    }

    //執(zhí)行完真正的handler之后
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
    }

    // 視圖渲染之后
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable Exception ex) throws Exception {
    }

}
image.gif

HandlerInterceptor接口一共提供了三個接口分別為

  1. 獲取到Handler適配器,調(diào)用真正的 Handler處理類之前
  2. 執(zhí)行完真正的Handler處理類,但在視圖渲染之前
  3. 視圖渲染之后

三、Spring是如何裝載攔截器

1、XML 配置方式

在通通過 <mvc:interceptors> 方式配置攔截器,此方式是通過 標簽解析類 InterceptorsBeanDefinitionParser 完成的,具體如源碼所示

image
image.gif

?

2、SpringBoot注解方式

SpringBoot的的方式是需要先創(chuàng)建實現(xiàn)接口 WebMvcConfigurer 的自定也配置類,并且重寫addInterceptors方法,如下所示:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    //實現(xiàn)此方法,注冊自定義攔截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTHandlerInterceptor());
        registry.addWebRequestInterceptor(new TestWebRequestInterceptor());
    }
}
image.gif

此處注冊了攔截器,那么在真正執(zhí)行時,又是如何獲取攔截器的呢?

在上篇介紹 Spring MVC 源碼分析之 加載及查找 Controller 說明 RequestMappingHandlerMapping 的創(chuàng)建時,有說明??梢宰孕胁榭础?/p>

四、執(zhí)行順序

如果自定義了多個攔截器,如何使其按自己的順序執(zhí)行呢,這個在 WebMvcConfigurer中注冊攔截器時,可以按照自己的執(zhí)行順序進行注冊,有一點需要注意,在攔截器中執(zhí)行 preHandle 方法是按注冊順序執(zhí)行,在執(zhí)行 postHandle 方法時是按照注冊的倒序進行執(zhí)行的。

攔截器的注冊順序為 攔截器1,攔截器2,攔截器3,

執(zhí)行 preHandle 方法的順序為:攔截器1 =》攔截器2 =》 攔截器3

執(zhí)行 postHandle 方法的順序為:攔截器3 =》攔截器2 =》 攔截器1

五、總結

此篇文章主要介紹了,攔截器的注冊執(zhí)行等,介紹的比較粗糙,主要為了記錄一下自己的源碼分析。望大拿斧正。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容