天天看點

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

SpringMVC HandlerInterceptor攔截器源碼解析

文章目錄

  • SpringMVC HandlerInterceptor攔截器源碼解析
  • 一、執行個體
    • 1.單個攔截器
        • 單個攔截器的執行順序
          • return true并且無異常
          • return false 後續内容将會被攔截,不再執行
          • return true 有異常 仍會執行afterCompletion,但不執行postHandle()
    • 2.多個攔截器
      • 多個攔截器執行順序
  • 二、源碼 DispatcherServlet.class中尋找原因
    • 1.找到preHandle()執行的位置
    • 2.找到preHandle()執行的位置
    • 3.afterCompletion相關

一、執行個體

寫一個類繼承HandlerInterceptor接口,重寫其中的方法

1.單個攔截器

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

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

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

注冊

<!-- 攔截器  -->
    <mvc:interceptors>
        <bean class="edu.qust.controller.MyFirstInterceptor"></bean>
    </mvc:interceptors>
           

Controller

@Controller
public class InterceptorTestController {
    @RequestMapping("/test01")
    public String test01(){
        System.out.println("test01方法調用");
        return "success";
    }
}
           

執行順序

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

單個攔截器的執行順序

return true并且無異常

preHandle ⇒ 調用方法 ⇒ postHandle ⇒ 到達頁面 ⇒ afterCompletion

return false 後續内容将會被攔截,不再執行
@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle.....");
        return false;
    }
           
SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因
return true 有異常 仍會執行afterCompletion,但不執行postHandle()
SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因
SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

2.多個攔截器

執行順序取決于配置順序

配置順序,這裡是先配置的2号攔截器,即preHandle…222

<!-- 攔截器  -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/test01"/>
            <bean class="edu.qust.controller.MySecondInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

    <mvc:interceptors>
        <bean class="edu.qust.controller.MyFirstInterceptor"></bean>
    </mvc:interceptors>
           

新增的secondInterceptor

public class MySecondInterceptor implements HandlerInterceptor {

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

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

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

}
           

兩個preHandle都return true的情況

雖然名字是2号,但因為配置順序是先2後1,是以會先執行2

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

多個攔截器執行順序

(先配置的)preHandle ⇒ (後配置的)preHandle ⇒ 調用方法 ⇒ (後配置的)postHandle ⇒ (先配置的)postHandle ⇒ 到達頁面 ⇒ (後配置的)afterCompletion ⇒ (先配置的)afterCompletion

preHandle()==>誰配置在前誰先,post/after ⇒ 的誰配置在後誰先

上面是兩個攔截器的handle都return true的情況,現在設定return false的情況

讓2号return true ,1号 return false的情況

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

發現2号的afterCompletion執行了 ==>放行preHandle的會執行自己的afterCompletion

是以沒有放行的1号并沒有執行自己的afterCompletion

那麼為什麼會這樣執行,放行了preHandle的攔截器為什麼會執行自身的afterCompletion

二、源碼 DispatcherServlet.class中尋找原因

1.找到preHandle()執行的位置

找到在DispatcherServlet.class實際幹活的doDispatch(),開始debug

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

在執行getHandler中發現,拿到的不隻是可以用來處理的類handler,還有攔截器集合interceptorList

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

發現其中有3個攔截器

還有一個預設為-1的攔截器索引

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

1和2是我們自己設計的攔截器

接着向下執行

執行到了下面這句時,控制台列印了

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因
SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

看英文能看出來 applyPreHandle 就是 執行preHandle嘛

此時我們找到了攔截器執行preHandle()的位置

這下得進去看看為什麼放行了preHandle就會執行他的afterCompletion

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }
        return true;
    }
           

這時候之前拿到的攔截器集合就出現了,可以發現是做周遊攔截器的操作,如果目前攔截器的preHandle傳回的是false則會執行if語句裡的内容 ===> 調用triggerAfterCompletion,然後return false直接結束

這裡要注意 :源碼中的 triggerAfterCompletion ≠ afterCompletion

這裡很容易誤會成傳回false才執行afterCompletion,這不就成了傳回false才執行afterCompletion了嗎? 然而他們倆并不是一個方法,上面的例子很明顯說明了

preHandle傳回了true才執行自身的afterCompletion
           

看for循環中,出現了之前的攔截器索引 interceptorIndex ,之前這個數值為 -1 ,現在做自增循環,很明顯,這是在記錄個數,以本身為下标,一直+1 ,直到出現了第一個return false的攔截器出現。

周遊的順序就是配置的順序

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

在執行到第3個(下标為2)的MyFirstInterceptor時,因為設定了return false,是以執行了if中的語句

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

進入this.triggerAfterCompletion(request, response, (Exception)null);

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = this.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 var8) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
                }
            }
        }

    }
           

發現還是在周遊之前的攔截器集合,但這次看到了真正的afterCompletion

看這次的for循環,i是基于之前interceptorIndex做自減循環,而interceptorIndex則是用來記錄有多少return true的攔截器,本身就是數組的下标。

這下就清楚了,這是把interceptorIndex作為下标,把集合中interceptorIndex之前的攔截器的afterCompletion都執行一遍。是以就實作了return true的會執行afterCompletion的效果

是以return true的2号會列印afterCompletion,而return false的1号則沒有執行

現在把兩個都改成return true再debug,不然下面會被攔截不執行

找完問題的原因後繼續向下走,接着執行

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

(mv的源碼分析補在以後)

2.找到preHandle()執行的位置

繼續執行

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因
SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

找到了執行postHandle的位置

進去看看如何執行postHandle()

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.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);
            }
        }

    }
           

發現依舊是周遊,但是這次是倒叙周遊,這也就說明了為什麼先配置的攔截器會後執行postHandle()

回到doDispatch(),繼續走,進入有渲染頁面方法的processDispatchResult()中

3.afterCompletion相關

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }

        if (mv != null && !mv.wasCleared()) {
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isTraceEnabled()) {
            this.logger.trace("No view rendering, null ModelAndView returned.");
        }

        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }

        }
    }
           

執行到

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

進行頁面渲染,控制台列印

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

若this.render正常執行,接着則執行triggerAfterCompletion去執行afterCompletion

if (mappedHandler != null) {
    mappedHandler.triggerAfterCompletion(request, response, (Exception)null);}
           

若this.render有問題,下面的catch也會捕獲,接着執行triggerAfterCompletion,這就是為什麼i=1/0這種異常程式依舊會執行afterCompletion的原因

SpringMVC HandlerInterceptor攔截器解析及源碼SpringMVC HandlerInterceptor攔截器源碼解析一、執行個體二、源碼 DispatcherServlet.class中尋找原因

繼續閱讀