Spring MVC請求處理(五) - 攔截器

DispatcherServlet的doDispatch方法在調(diào)用處理器處理請求前后分別調(diào)用了攔截器的前置和后置處理方法,代碼如下所示:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
    return;
}

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

processDispatchResult方法在渲染后調(diào)用了攔截器的完成處理方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

    // 省略一些代碼

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

不管調(diào)用攔截器的哪個方法,都與攔截器自己和HandlerExecutionChain相關(guān),本文接下來分析攔截器和HandlerExecutionChain。

攔截器HandlerInterceptor

攔截器接口HandlerInterceptor允許定制化請求的處理過程,其代碼如下所示:

public interface HandlerInterceptor {

    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;
}

三個接口方法的用途和區(qū)別如下:

  • preHandle前置處理方法在處理器被調(diào)用前執(zhí)行,只有返回true時后續(xù)攔截器的前置處理方法才會執(zhí)行;
  • postHandle后置處理方法在處理器被調(diào)用后且渲染前執(zhí)行,會按添加的順序逆序執(zhí)行;
  • afterCompletion完成處理方法在請求處理完成后即渲染后執(zhí)行,同樣會按添加的順序逆序執(zhí)行,注意只有前置處理方法返回true時該方法才會被執(zhí)行。

HandlerExecutionChain

在分析HandlerMapping時提到,HandlerMapping的getHandler會將匹配的處理器和攔截器包裝成HandlerExecutionChain并返回,接下來看一下HandlerExecutionChain類。

成員變量

public class HandlerExecutionChain {

    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

    private final Object handler;

    private HandlerInterceptor[] interceptors;

    private List<HandlerInterceptor> interceptorList;

    private int interceptorIndex = -1;

    public HandlerExecutionChain(Object handler) {
        this(handler, (HandlerInterceptor[]) null);
    }

    public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
        if (handler instanceof HandlerExecutionChain) {
            HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
            this.handler = originalChain.getHandler();
            this.interceptorList = new ArrayList<HandlerInterceptor>();
            CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
            CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
        }
        else {
            this.handler = handler;
            this.interceptors = interceptors;
        }
    }

    public Object getHandler() {
        return this.handler;
    }

    public void addInterceptor(HandlerInterceptor interceptor) {
        initInterceptorList().add(interceptor);
    }

    public void addInterceptors(HandlerInterceptor... interceptors) {
        if (!ObjectUtils.isEmpty(interceptors)) {
            CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
        }
    }

    private List<HandlerInterceptor> initInterceptorList() {
        if (this.interceptorList == null) {
            this.interceptorList = new ArrayList<HandlerInterceptor>();
            if (this.interceptors != null) {
                // An interceptor array specified through the constructor
                CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
            }
        }
        this.interceptors = null;
        return this.interceptorList;
    }

    public HandlerInterceptor[] getInterceptors() {
        if (this.interceptors == null && this.interceptorList != null) {
            this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
        }
        return this.interceptors;
    }

    // 省略一些代碼
}
  • handler即是已匹配的處理器對象;
  • interceptors和interceptorList都用來保存關(guān)聯(lián)的攔截器,前者是數(shù)組,后者是列表;
  • interceptorIndex是一個索引,下面會看到其作用。

請求前置處理

HandlerExecutionChain的前置處理方法在處理器對象handler被調(diào)用前執(zhí)行,它會調(diào)用所有與該HandlerExecutionChain關(guān)聯(lián)的攔截器的前置處理方法:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; 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按添加順序依次被調(diào)用,若其中某個攔截器返回false則終止攔截器鏈的執(zhí)行過程,觸發(fā)請求完成處理;
  • interceptorIndex變量保存的是preHandle方法已執(zhí)行且返回true的攔截器在數(shù)組中的索引;
  • 只有HandlerExecutionChain的前置處理方法返回true后處理器才會被真正調(diào)用。

請求后置處理

HandlerExecutionChain的后置處理方法在處理器被調(diào)用后、渲染之前執(zhí)行,它會調(diào)用所有與該HandlerExecutionChain關(guān)聯(lián)的攔截器的后置處理方法:

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
    HandlerInterceptor[] interceptors = 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按添加順序反向依次被調(diào)用。

請求完成處理

HandlerExecutionChain的完成處理方法在請求處理完成后即渲染之后執(zhí)行,它會調(diào)用所有與該HandlerExecutionChain關(guān)聯(lián)的preHandle方法已執(zhí)行且返回true的攔截器的完成處理方法:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
        throws Exception {
    HandlerInterceptor[] interceptors = 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 ex2) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
        }
    }
}
  • interceptorIndex變量在這里發(fā)揮作用,所以triggerAfterCompletion與applyPostHandle的區(qū)別是前者不會觸發(fā)所有的攔截器,只觸發(fā)preHandle方法已執(zhí)行且返回true的攔截器,而后者會觸發(fā)所有的攔截器;
  • preHandle方法已執(zhí)行且返回true的各攔截器的完成處理方法afterCompletion按添加順序反向被調(diào)用。

添加攔截器

在Spring中可以繼承WebMvcConfigurerAdapter類并重寫addInterceptors方法添加所需的攔截器:

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyIntercepter());
    }
}

總結(jié)

至此,我們終于大概知道了Spring MVC對請求的處理過程,在這其中更深入地了解了Spring MVC的運(yùn)行機(jī)制和擴(kuò)展接口,也看到了諸如模板方法模式、策略模式和組合模式等設(shè)計模式的實(shí)際運(yùn)用。

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

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

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