SpringMVC doDispatch方法的基本思路梳理

Posted HelloWorld_EE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC doDispatch方法的基本思路梳理相关的知识,希望对你有一定的参考价值。

SpringMVC doDispatch方法的基本思路梳理

在分析之前,先介绍一个类

1、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;

该类中主要包括了一个用于处理request的handler,以及和request相关的一系列拦截器interceptors.

进入正题,doDispatch方法的代码如下:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception 
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try 
            try 
                ModelAndView mv = null;
                Exception dispatchException = null;

                try 
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    /*
                    分析1,其目的是:得到包含了用于处理request的HandlerMethod、Interceptors。
                    */
                    mappedHandler = this.getHandler(processedRequest, false);
                    //如果没有,则说明没有处理器能够处理该request,因此返回404错误。
                    if(mappedHandler == null || mappedHandler.getHandler() == null) 
                        this.noHandlerFound(processedRequest, response);
                        return;
                    
                        /*
                        分析2,其目的是:得到与Handler匹配的HandlerAdapter。
                        */
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if(isGet || "HEAD".equals(method)) 
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if(this.logger.isDebugEnabled()) 
                            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        

                        if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) 
                            return;
                        
                    
                        /*
                        分析3:执行所有注册拦截器的preHandler方法
                        */
                    if(!mappedHandler.applyPreHandle(processedRequest, response)) 
                        return;
                    

                    try 
                    //前面的第一步就是找到用于处理request的handler,到这里才真正意思上的执行用于处理request的handler方法。具体后面进行分析
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                     finally 
                        if(asyncManager.isConcurrentHandlingStarted()) 
                            return;
                        

                    

                    this.applyDefaultViewName(request, mv);
                    /*
                    分析4:执行所有注册拦截器的postHandler方法
                    */
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                 catch (Exception var27) 
                    dispatchException = var27;
                

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
             catch (Exception var28) 
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var28);
             catch (Error var29) 
                this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var29);
            

         finally 
            if(asyncManager.isConcurrentHandlingStarted()) 
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                return;
             else 
                if(multipartRequestParsed) 
                    this.cleanupMultipart(processedRequest);
                

            
        
    

doDispatch方法主要逻辑如下:

1)找到用于处理request的handler和interceptors,构造成一个HandlerExecutionChain.

2) 找到与用于处理request的handler相匹配的HandlerAdapter,目的是:后续将使用此HandlerAdapter来执行handler。

3)执行所有注册拦截器的preHandler方法

4)真正意思上的执行用于request的handler方法,得到ModelAndView

5)倒序执行所有注册拦截器的postHandler方法。

6)解析并返回ModelAndView

主要是理清思路,本篇博文将依次分析除第4点之外的几点。

分析1:找到用于处理request的handler和interceptors,构造成一个HandlerExecutionChain.

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception 
        for (HandlerMapping hm : this.handlerMappings) 
            if (logger.isTraceEnabled()) 
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) 
                return handler;
            
        
        return null;
    

该函数的功能为:遍历DispatcherServlet中的private List handlerMappings; 看哪个HandlerMapping能够处理该request,则返回由

AbstractHandlerMapping.java

    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception 
        Object handler = getHandlerInternal(request);//第一步
        if (handler == null) 
            handler = getDefaultHandler();
        
        if (handler == null) 
            return null;
        
        // Bean name or resolved handler?
        if (handler instanceof String) 
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        
        //第二步
        return getHandlerExecutionChain(handler, request);
    

此方法的主要逻辑如下:

第一步:根据request中的url得到相应的handler,此handler为HandlerMethod的实例。

第二步:将第一步得到的HandlerMethod作为参数实例化一个HandlerExecutionChain对象。在实例化过程中,还需要得到与request相关的Interceptors。

下面将逐一的分析这两步所涉及到具体实现。

先看第一步:根据request中的url得到相应的handler,此handler为HandlerMethod的实例

getHandlerInternal方法的代码如下:

    AbstractHandlerMethodMapping.java   

    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception 
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        if (logger.isDebugEnabled()) 
            logger.debug("Looking up handler method for path " + lookupPath);
        
        //得到与request相关的handlerMethod,如何得到的,就是在Map中来寻找
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        if (logger.isDebugEnabled()) 
            if (handlerMethod != null) 
                logger.debug("Returning handler method [" + handlerMethod + "]");
            
            else 
                logger.debug("Did not find handler method for [" + lookupPath + "]");
            
        
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
       

getHandlerInternal函数的功能:就是在Map中寻找HandlerMethod方法。Map中保存的是<url,HandlerMethod>,Map中的内容如何来的呢:是在容器初始化时会建立所有url和controller的对应关系,保存到Map

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

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

        return chain;
    

其中,private final List<MappedInterceptor> mappedInterceptors =
new ArrayList<MappedInterceptor>();
中保存的是我们项目配置文件配置的拦截器。

分析2:找到与用于处理request的handler相匹配的HandlerAdapter

DispatcherServlet.java

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException 
        for (HandlerAdapter ha : this.handlerAdapters) 
            if (logger.isTraceEnabled()) 
                logger.trace("Testing handler adapter [" + ha + "]");
            
            if (ha.supports(handler)) 
                return ha;
            
        
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        

该getHandlerAdapter函数的功能为:判断private List<HandlerAdapter> handlerAdapters;中哪个Adapter能支持这个handler。或者说,这个handler应该与哪个Adapter进行搭档才能正常工作。

从Debug的情况来看,如果我们采用的是@RequestMapping注解形式,则获取到的是HandlerAdapter 是RequestMappingHandlerAdapter类的一个实例。

以下就是RequestMappingHandlerAdapter的support方法的实现,即只要此时的handler为HandlerMethod的实例,就是支持的。

    AbstractHandlerMethodAdapter.java
    public final boolean supports(Object handler) 
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    
    RequestMappingHandlerAdapter    
    protected boolean supportsInternal(HandlerMethod handlerMethod) 
        return true;
    

分析3:执行所有注册拦截器的preHandler方法

    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;//interceptorIndex标示着拦截器数组中下标小于等于interceptorIndex的拦截器已经被成功执行。
            
        
        return true;
    

该函数的功能为:调用所有注册拦截器的preHandle方法,如果preHandle方法的返回结果为true,则会继续执行下面的程序。如果preHandler方法返回false,则调用triggerAfterCompletion方法。该方法的代码如下:

    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);
                
            
        
    

从代码中可以看到:triggerAfterCompletion方法它会调用所有已经成功执行的拦截器的afterCompletion方法,而且是反序调用的过程。

以上可以得到的结论为:

1)只有所有注册的拦截器都执行成功,即都返回true,则doDispathcer中的代码才会继续执行。

2)如果第i个拦截器返回false,则会触发triggerAfterCompletion方法来反序执行刚刚所有已经成功执行的拦截器的afterCompletion方法。并返回false从而使得doDispatcher中的代码就不会往下执行了。

看到这里,我们也就明白了拦截器的作用。

分析4:倒序执行所有注册拦截器的postHandler方法

    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);
            
        
    

该函数的功能:反序执行所有注册的拦截器的postHandler方法,比较简单哈。

小结

就这样我们稍微理清了下doDispatch这个方法的基本思路,有很多细节,博文中并没有关注,如果你有兴趣,可以仔细研究。

========================漂亮的分割线========================

博文的以上部分可以看作是SpringMVC用于处理Http请求的运行主线的源码分析。而没有分析初始化主线。在看了这篇博文之后(http://downpour.iteye.com/blog/1341459),感觉对SpringMVC的初始化主线又多了一份了解。

总结如下:

1、基于框架所编写的应用程序的构成三要素。

a)入口程序DispatcherServlet,在web.xml文件中定义。

b)核心配置文件,[servlet-name]-servlet.xml文件。里面定义了相关组件。

c)控制逻辑,Controller。

2、框架的具体实现。

有两条主线。

a)初始化主线 -- 负责对SpringMVC的运行要素进行初始化。

b)用于处理http请求的运行主线 -- 负责对SpringMVC中的组件进行逻辑调度完成对Http请求的处理。

这两条主线的划分是根据servlet中的init方法和service方法来的,这两个方法的运行时间和触发条件截然不同。其中,init方法在整个系统启动时运行,且只运行一次。因此,在init方法中我们往往会对整个应用程序进行初始化操作。而service方法在整个系统运行的过程中一直处于监听模式,侦听并处理所有的web请求。

SpringMVC的整个运行体系,是由DispatcherServlet、组件和容器这三者共同构成的。 既然是三个元素之间的关系表述,我们必须以两两关系的形式进行归纳:

DispatcherServlet - 容器 —— DispatcherServlet负责对容器进行初始化,初始化的依据是核心配置文件

容器 - 组件 —— 容器对组件进行全局管理

DispatcherServlet - 组件 —— DispatcherServlet对组件进行逻辑调用

2.1)初始化主线。

首先要明确3个观点。

a)初始化主线的驱动方法:init().

b) 初始化主线的执行次序(根据DispatcherServlet的继承结构而来):HttpServletBean–>FrameworkServlet–>DispatherServlet.

c) 初始化主线的操作对象:Spring容器(WebApplicatonContext)以及组件.

(配图)

2.2)用于处理http请求的运行主线。

(配图)

以上是关于SpringMVC doDispatch方法的基本思路梳理的主要内容,如果未能解决你的问题,请参考以下文章

SpringMVC源码解析-doDispatch方法

SpringMVC源码-执行流程

springmvc的问题,URL分号之后的内容读不到这是为啥呢?

源码分析spring-mvc启动流程

浅谈SpringMVC源码的DispatcherServlet组件执行流程

springmvc学习笔记-前端控制器