美文网首页
Spring MVC请求处理(五) - 拦截器

Spring MVC请求处理(五) - 拦截器

作者: buzzerrookie | 来源:发表于2019-03-23 14:21 被阅读0次

    DispatcherServlet的doDispatch方法在调用处理器处理请求前后分别调用了拦截器的前置和后置处理方法,代码如下所示:

    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方法在渲染后调用了拦截器的完成处理方法:

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
    
        // 省略一些代码
    
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }
    

    不管调用拦截器的哪个方法,都与拦截器自己和HandlerExecutionChain相关,本文接下来分析拦截器和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;
    }
    

    三个接口方法的用途和区别如下:

    • preHandle前置处理方法在处理器被调用前执行,只有返回true时后续拦截器的前置处理方法才会执行;
    • postHandle后置处理方法在处理器被调用后且渲染前执行,会按添加的顺序逆序执行;
    • afterCompletion完成处理方法在请求处理完成后即渲染后执行,同样会按添加的顺序逆序执行,注意只有前置处理方法返回true时该方法才会被执行。

    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都用来保存关联的拦截器,前者是数组,后者是列表;
    • interceptorIndex是一个索引,下面会看到其作用。

    请求前置处理

    HandlerExecutionChain的前置处理方法在处理器对象handler被调用前执行,它会调用所有与该HandlerExecutionChain关联的拦截器的前置处理方法:

    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按添加顺序依次被调用,若其中某个拦截器返回false则终止拦截器链的执行过程,触发请求完成处理;
    • interceptorIndex变量保存的是preHandle方法已执行且返回true的拦截器在数组中的索引;
    • 只有HandlerExecutionChain的前置处理方法返回true后处理器才会被真正调用。

    请求后置处理

    HandlerExecutionChain的后置处理方法在处理器被调用后、渲染之前执行,它会调用所有与该HandlerExecutionChain关联的拦截器的后置处理方法:

    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按添加顺序反向依次被调用。

    请求完成处理

    HandlerExecutionChain的完成处理方法在请求处理完成后即渲染之后执行,它会调用所有与该HandlerExecutionChain关联的preHandle方法已执行且返回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变量在这里发挥作用,所以triggerAfterCompletion与applyPostHandle的区别是前者不会触发所有的拦截器,只触发preHandle方法已执行且返回true的拦截器,而后者会触发所有的拦截器;
    • preHandle方法已执行且返回true的各拦截器的完成处理方法afterCompletion按添加顺序反向被调用。

    添加拦截器

    在Spring中可以继承WebMvcConfigurerAdapter类并重写addInterceptors方法添加所需的拦截器:

    @Configuration
    public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyIntercepter());
        }
    }
    

    总结

    至此,我们终于大概知道了Spring MVC对请求的处理过程,在这其中更深入地了解了Spring MVC的运行机制和扩展接口,也看到了诸如模板方法模式、策略模式和组合模式等设计模式的实际运用。

    相关文章

      网友评论

          本文标题:Spring MVC请求处理(五) - 拦截器

          本文链接:https://www.haomeiwen.com/subject/oftsvqtx.html