天天看點

springmvc攔截器工作源碼

當一個請求到達後,會調用DispatcherServlet的doDispatch( )方法,在此方法中調用攔截器,調用方法棧如下:

springmvc攔截器工作源碼

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方法。