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";
}
}
執行順序
單個攔截器的執行順序
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;
}
return true 有異常 仍會執行afterCompletion,但不執行postHandle()
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
多個攔截器執行順序
(先配置的)preHandle ⇒ (後配置的)preHandle ⇒ 調用方法 ⇒ (後配置的)postHandle ⇒ (先配置的)postHandle ⇒ 到達頁面 ⇒ (後配置的)afterCompletion ⇒ (先配置的)afterCompletion
preHandle()==>誰配置在前誰先,post/after ⇒ 的誰配置在後誰先
上面是兩個攔截器的handle都return true的情況,現在設定return false的情況
讓2号return true ,1号 return false的情況
發現2号的afterCompletion執行了 ==>放行preHandle的會執行自己的afterCompletion
是以沒有放行的1号并沒有執行自己的afterCompletion
那麼為什麼會這樣執行,放行了preHandle的攔截器為什麼會執行自身的afterCompletion
二、源碼 DispatcherServlet.class中尋找原因
1.找到preHandle()執行的位置
找到在DispatcherServlet.class實際幹活的doDispatch(),開始debug
在執行getHandler中發現,拿到的不隻是可以用來處理的類handler,還有攔截器集合interceptorList
發現其中有3個攔截器
還有一個預設為-1的攔截器索引
1和2是我們自己設計的攔截器
接着向下執行
執行到了下面這句時,控制台列印了
看英文能看出來 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的攔截器出現。
周遊的順序就是配置的順序
在執行到第3個(下标為2)的MyFirstInterceptor時,因為設定了return false,是以執行了if中的語句
進入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,不然下面會被攔截不執行
找完問題的原因後繼續向下走,接着執行
(mv的源碼分析補在以後)
2.找到preHandle()執行的位置
繼續執行
找到了執行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);
}
}
}
執行到
進行頁面渲染,控制台列印
若this.render正常執行,接着則執行triggerAfterCompletion去執行afterCompletion
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);}
若this.render有問題,下面的catch也會捕獲,接着執行triggerAfterCompletion,這就是為什麼i=1/0這種異常程式依舊會執行afterCompletion的原因