當一個請求到達後,會調用DispatcherServlet的doDispatch( )方法,在此方法中調用攔截器,調用方法棧如下:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQ3chVEa0V3bT9CX5RXa2Fmcn9CXwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwlaG5mYslTbhZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TM3gDMwETNyEjMygDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
doDispatch( )方法源碼如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
HandlerExecutionChain mappedHandler = null;
...
//執行interceptor的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//執行實際的Controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
//執行interceptor的postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
...
//渲染頁面并執行afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
...
}
HandlerExecutionChain是一個由攔截器組成的一個chain鍊,是一個責任鍊模式,内部儲存了攔截器的集合,這個集合和filter過濾器一樣,是在請求到來後,根據url進行正則比對,從所有的攔截器中選出符合規則的,然後加入到集合中。
public class HandlerExecutionChain {
private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -;
...
HandlerExecutionChain的applyHandle方法如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = ; 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;
}
執行邏輯是:按照攔截器的順序從前向後執行preHandle方法,如果有一個攔截器傳回了false,那麼将不再繼續執行目前攔截器及其之後攔截器的preHandle,而是按攔截器逆序執行afterCompletion方法:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= ; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
triggerAfterCompletion方法會吞掉所有異常,保證攔截器的afterCompletion方法全部得到執行。注意triggerAfterCompletion方法的循環邊界,是 interceptorIndex,而不是 interceptors.length,也就是說隻執行那些preHandle方法傳回true的攔截器。之後return false。不再繼續執行真正的Controller。
如果攔截器均傳回了true,那麼繼續執行Controller的邏輯,然後按逆序執行攔截器的postHandle方法:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - ; i >= ; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
如果在執行preHandle和postHandle時抛出了異常,那麼會把異常記錄下來,然後執行processDispatchResult方法;如果一切順利,那麼也會執行processDispatchResult方法:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
...
//處理錯誤頁面
mv = processHandlerException(request, response, handler, exception);
...
}
...
//寫回浏覽器結果
render(mv, request, response);
...
//執行攔截器的afterCompletion方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
總結一下:當一個請求到達之後,springmvc會根據請求的url找出相比對的攔截器,組裝成一個責任鍊,按照攔截器順序依次執行其preHandle方法,執行完畢之後,繼續執行具體的Controller,當Controller執行完成後,會逆序執行攔截器的postHandle方法,之後渲染頁面,最後逆序 執行afterCompletion方法。如果在執行preHandle的過程中,有任意一個interceptor傳回了false,那麼請求将不再繼續向下執行,在逆序執行完之前傳回true的攔截器的afterCompletion方法後,結束對請求的處理。如果在preHandle和postHandle方法中抛出了異常,那麼spring會将異常捕捉,記錄異常資訊,不再繼續執行攔截器的處理,而是執行渲染頁面并執行那些成功執行過preHandle方法的攔截器的afterCompletion方法。