SpringMvc拦截器运行原理

首先,先简单的说一下怎么配置SpringMvc的拦截器。

分两步,第一步先定义一个类,实现HandlerInterceptor接口。

import javax.servlet.http.HttpServletRequest;
 javax.servlet.http.HttpServletResponse;

 org.springframework.web.servlet.HandlerInterceptor;
 org.springframework.web.servlet.ModelAndView;

public class TestInterceptor implements HandlerInterceptor{

    @Override
    boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)
            throws Exception {
        System.out.println("preHandle invoke");
        return true;
    }

    @Override
    void postHandle(HttpServletRequest request,Object handler,ModelAndView modelAndView)  Exception {
        System.out.println("postHandle invoke");
    }

    @Override
     afterCompletion(HttpServletRequest request,Exception ex)
             Exception {
        System.out.println("afterCompletion invoke");
        
    }

}

第二布,配置springMvc.xml

<!-- 拦截器 -->
    <mvc:interceptors>
        mvc:interceptor>
            mvc:mapping path="/*"/>
            bean class="com.mmc.interceptor.TestInterceptor"></bean</>
    >

完工。下面分析原理

打开这个DispatcherServlet类,这个类是SpringMvc中最核心的一个类。答案就在doDispatch方法里面:

protected void doDispatch(HttpServletRequest request,HttpServletResponse response)  Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = ;
            Exception dispatchException = ;

             {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = processedRequest != request;

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == ) {
                    noHandlerFound(processedRequest,response);
                    return;
                }

                 Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                 Process last-modified header,if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request,mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        String requestUri = urlPathHelper.getRequestUri(request);
                        logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request,response).checkNotModified(lastModified) && isGet) {
                        ;
                    }
                }

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

                 {
                     Actually invoke the handler.
                    mv = ha.handle(processedRequest,response,mappedHandler.getHandler());
                }
                finally (asyncManager.isConcurrentHandlingStarted()) {
                        ;
                    }
                }

                applyDefaultViewName(request,mv);
                mappedHandler.applyPostHandle(processedRequest,mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            processDispatchResult(processedRequest,mappedHandler,mv,dispatchException);
        }
         (Exception ex) {
            triggerAfterCompletion(processedRequest,ex);
        }
         (Error err) {
            triggerAfterCompletionWithError(processedRequest,err);
        }
         {
             (asyncManager.isConcurrentHandlingStarted()) {
                 Instead of postHandle and afterCompletion
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest,response);
                ;
            }
             Clean up any resources used by a multipart request.
             (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }

里面我标红了四处,第一处对应执行自定义拦截器的preHandler方法,第二处对应执行你的Controller类中的RequestMapping映射的处理方法,第三处是自定义拦截器的postHandle方法,第四处是自定义拦截器的afterCompletion方法。

 

这些可能很多人都知道,但是我们肯定还有疑问,为什么他能调用到我的拦截器里写的方法呢?

 接着往下分析。

 

于是我们点开我第一处标红那里的mappedHandler.applyPreHandle方法。代码如下:

boolean applyPreHandle(HttpServletRequest request,1)"> Exception {
        if (getInterceptors() != ) {
            for (int i = 0; i < getInterceptors().length; i++) {
                HandlerInterceptor interceptor = getInterceptors()[i];
                if (!interceptor.preHandle(request,this.handler)) {
                    triggerAfterCompletion(request,);
                    ;
                }
                this.interceptorIndex = i;
            }
        }
        ;
    }

看得出来,他是在遍历getInterceptors(),所以在点开这个方法。

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

这里面有两个变量,interceptorList和interceptors。这时我肯定会猜想,肯定是某个时候系统框架把这两个变量赋值了,然后在我执行方法的时候都会去看看这两个变量是不是有值的。有就执行拦截器。

那么这两个变量是什么时候赋值的呢?找到定义这两个变量的地方,他们是在HandlerExecutionChain类里面,我把断点打在这个变量定义那里。debug运行,代码停在了HandlerExecutionChain里面

 

 

 看debug模式中的方法执行顺序,点到doDispatch方法里面,在执行applyPreHandle即调用拦截器的方法之前,有一个getHandler方法,这个方法里面有getHandlerExecutionChain方法。这个方法里面有如下代码:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler,HttpServletRequest request) {
        HandlerExecutionChain chain =
            (handler instanceof HandlerExecutionChain) ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);

        chain.addInterceptors(getAdaptedInterceptors());

        String lookupPath = urlPathHelper.getLookupPathForRequest(request);
        for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
             (mappedInterceptor.matches(lookupPath,pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }

         chain;
    }

注意看,里面有一句chain.addInterceptor(mappedInterceptor.getInterceptor());这个从名字就知道是添加拦截器。而这个拦截器是从mappedInterceptor获得的属性,mappedInterceptor是遍历mappedInterceptors得到的。而这个对象在我运行这个的时候就已经存在了。所以我思考是不是启动项目的时候就已经初始化了,而且是根据我的xml配置文件来初始化的。于是我在mappedInterceptors对象处打断点,启动tomcat。果然代码执行到了。在AbstractHandlerMapping类中找到了这个方法,

@Override
    void initApplicationContext()  BeansException {
        extendInterceptors(.interceptors);
        detectMappedInterceptors(.mappedInterceptors);
        initInterceptors();
 

 在这个detectMappedInterceptors(this.mappedInterceptors);里面便是将mappedInterceptors对象赋值了。下面我就不贴代码了。就是去找MappedInterceptor类,这个类是根据xml文件中<mvc:interceptor>的内容来构建的。构建方法在InterceptorsBeanDefinitionParser类里面。

顺着来一遍就是这样的:

 启动项目,InterceptorsBeanDefinitionParser类会根据配置文件构建MappedInterceptor对象集合,当我发送请求的时候,系统去看这个集合里有没有值,如果有值,就遍历执行这个对象,取得这个对象中的HandlerInterceptor属性,也就是我自定义的拦截器的对象。然后去执行我这个拦截器中的三个方法。到此拦截器的运行原理就分析完了。

 

有分析不对的地方请不吝指出



 

相关文章

这篇文章主要介绍了spring的事务传播属性REQUIRED_NESTED的原...
今天小编给大家分享的是一文解析spring中事务的传播机制,相...
这篇文章主要介绍了SpringCloudAlibaba和SpringCloud有什么区...
本篇文章和大家了解一下SpringCloud整合XXL-Job的几个步骤。...
本篇文章和大家了解一下Spring延迟初始化会遇到什么问题。有...
这篇文章主要介绍了怎么使用Spring提供的不同缓存注解实现缓...