美文网首页
SpringMVC源码的研究之dispatcherServlet

SpringMVC源码的研究之dispatcherServlet

作者: 代码potty | 来源:发表于2018-09-05 16:46 被阅读0次

    这几天调试项目,突然对于http请求如何传到我的项目,然后找到对应的控制方法产生了兴趣,这部分的内容比较多,本来想用一篇长文来测试总结,现在还是先分点去记录。


    image.png

    首先先介绍一下请求传输到项目的流程。Http请求是属于应用层的,请求的传输其实是在传输层,通过socket进行传输到tomcat.
    先介绍一些关于tomcat的知识。
    tomcat目前的传输协议有四种,分别是:BIO, NIO,NIO2,APR

    BIO协议依赖的类是org.apache.coyote.http11.Http11Protocol
    这个协议下,分为两部分,第一部分是acceptor,用于接收socket请求的
    第二部分是worker线程池,用于处理socket请求的
    BIO是阻塞流的IO,acceptor接收socket请求后发送给worker线程池处理,如果线程池没有空闲的线程,那么acceptor就会发生阻塞。

    NIO协议依赖的类是org.apache.coyote.http11.Http11NioProtocol
    这个协议在BIO的基础上增加了一个poller设置
    acceptor接收socket请求后不是直接发送给线程池,而是发送给poller。在Poller中,维护了一个Selector对象;当Poller从队列中取出socket后,注册到该Selector中;然后通过遍历Selector,找出其中可读的socket,并使用Worker中的线程处理相应请求。
    通过BIO和NIO的描述,可以看出NIO在接收socket和处理socket的过程中,仍然会出现阻塞,不同的是,NIO增加了一个Poller,基于这个区别,如果tomcat在高分发的情况下,使用NIO可以提高Tomcat的工作效率

    目前大多数HTTP请求使用的是长连接(HTTP/1.1默认keep-alive为true),而长连接意味着,一个TCP的socket在当前请求结束后,如果没有新的请求到来,socket不会立马释放,而是等timeout后再释放。如果使用BIO,“读取socket并交给Worker中的线程”这个过程是阻塞的,也就意味着在socket等待下一个请求或等待释放的过程中,处理这个socket的工作线程会一直被占用,无法释放;因此Tomcat可以同时处理的socket数目不能超过最大线程数,性能受到了极大限制。而使用NIO,“读取socket并交给Worker中的线程”这个过程是非阻塞的,当socket在等待下一个请求或等待释放时,并不会占用工作线程,因此Tomcat可以同时处理的socket数目远大于最大线程数,并发性能大大提高。

    APR协议依赖的类是org.apache.coyote.http11.Http11AprProtocol
    APR是Apache Portable Runtime,是Apache可移植运行库,利用本地库可以实现高可扩展性、高性能;Apr是在Tomcat上运行高并发应用的首选模式

    配置相对应的协议:

    配置BIO的
    <Connector port="8080" protocol="org.apache.coyote.http11.Http11Protocol "
    connectionTimeout="20000"
    redirectPort="8443" />

    配置NIO的
    <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol "
    connectionTimeout="20000"
    redirectPort="8443" />

    配置APR的
    <Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol "
    connectionTimeout="20000"
    redirectPort="8443" />
    注:Tomcat7中支持这3种,Tomcat8增加了对NIO2的支持,而到了Tomcat8.5和Tomcat9.0,则去掉了对BIO的支持。
    如果没有指定Protocal,则使用默认值HTTP/1.1,其含义如下:在Tomcat7中,自动选取使用BIO或APR(如果找到ARP需要的本地库,则使用ARP,否则使用BIO;在Tomcat8,自动选取使用NIO或ARP(如果找到APR需要的本地库,则使用APR,否则使用NIO。
    参考链接:http://www.cnblogs.com/kismetv/p/7806063.html

    socket请求经过tomcat的线程处理后,会封装成RequestFacade然后到web.xml配置文件
    通过配置先初始化springMVC容器,进入DispatcherServlet类,进行相关的初始化工作,这部分初始化我只了解了一点点,先将我现在理解的写出来,进入这个类,会先调用它的有参或者无参构造方法,然后进入它的父类或者子类的方法初始化spring容器,将handlerMethod和url进行关联,也就是在dispatcherServlet中有个onRefresh方法会执行初始化方法initStrategies
    该方法的结构如下:

        protected void initStrategies(ApplicationContext context) {
               this.initMultipartResolver(context);
               this.initLocaleResolver(context);
               this.initThemeResolver(context);
               this.initHandlerMappings(context);
               this.initHandlerAdapters(context);
               this.initHandlerExceptionResolvers(context);
               this.initRequestToViewNameTranslator(context);
               this.initViewResolvers(context);
               this.initFlashMapManager(context);
           }
    

    我了解的是HandlerMappings这部分,将handlerMappings初始化好会先调用一开始结构图中的doService方法,然后才到doDispatch方法,doDispatch方法是springMVC的核心方法之一。

        rotected 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;
                      Object dispatchException = null;
    
                      try {
                            //判断request是否是文件上传的
                          processedRequest = this.checkMultipart(request);
                          multipartRequestParsed = processedRequest != request;
                          //通过HandlerMappings迭代器,选出合适的HandlerMapping,然后调用这个对象的getHandler获取Handler的执行链
                          mappedHandler = this.getHandler(processedRequest);
                          if (mappedHandler == null || mappedHandler.getHandler() == null) {
                              this.noHandlerFound(processedRequest, response);
                              return;
                          }
                          //mappedHandler.getHandler()类型是Object,但是可以转换成HandlerMethod,之前初始化讲到过这个部分
                          //通过handlerAdapter的方法supports来获取相对应的handlerAdapter(Hanlder的适配器)
                          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;
                              }
                          }
    
                          //测试所有的拦截器,有一个失败,则请求失败
                          if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                              return;
                          }
    
                          //绑定请求到视图上
                          mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                          if (asyncManager.isConcurrentHandlingStarted()) {
                              return;
                          }
    
                          this.applyDefaultViewName(processedRequest, mv);
                          mappedHandler.applyPostHandle(processedRequest, response, mv);
                      } catch (Exception var20) {
                          dispatchException = var20;
                      } catch (Throwable var21) {
                          dispatchException = new NestedServletException("Handler dispatch failed", var21);
                      }
    
                      //传输到前端页面
                      this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
                  } catch (Exception var22) {
                      this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
                  } catch (Throwable var23) {
                      this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
                  }
    
              } finally {
                  if (asyncManager.isConcurrentHandlingStarted()) {
                      if (mappedHandler != null) {
                          mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                      }
                  } else if (multipartRequestParsed) {
                      this.cleanupMultipart(processedRequest);
                  }
    
              }
          }
    
          private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {
              if (mv != null && !mv.hasView()) {
                  mv.setViewName(this.getDefaultViewName(request));
              }
    
          }
    

    这部分代码最重要的就是获取HandlerExecutorChain和HandlerAdaptor这俩部分

    HandlerExecutorChain是Handler的执行链,获取对应handler
    首先进行迭代HandlerMappings,然后根据传入的request,调用HandlerMapping的方法getHandler(request),
    该方法在AbstrateHandlerMapping类下,具体的代码如下:

        public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                //获取内部的handler
                Object handler = this.getHandlerInternal(request);
                if (handler == null) {
                    handler = this.getDefaultHandler();
                }
        
                if (handler == null) {
                    return null;
                } else {
                    if (handler instanceof String) {
                        String handlerName = (String)handler;
                        handler = this.getApplicationContext().getBean(handlerName);
                    }
        
                    HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
                    if (CorsUtils.isCorsRequest(request)) {
                        CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
                        CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
                        CorsConfiguration config = globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig;
                        executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
                    }
        
                    return executionChain;
                }
            }
    

    这部分代码中有个方法getHandlerInternal(request)方法是为了获取内部的handler的,这个方法在AbstrateHandlerMethodMapping类中,这部分代码如下:

     protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //获取request映射到的url地址
        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Looking up handler method for path " + lookupPath);
        }
      //设置读锁
        this.mappingRegistry.acquireReadLock();
        HandlerMethod var4;
        try {
          //取出该地址相关的handlerMethod
            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
            if (this.logger.isDebugEnabled()) {
                if (handlerMethod != null) {
                    this.logger.debug("Returning handler method [" + handlerMethod + "]");
                } else {
                    this.logger.debug("Did not find handler method for [" + lookupPath + "]");
                }
            }
    
            var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
        } finally {
            this.mappingRegistry.releaseReadLock();
        }
    
        return var4;
    }
    

    之前初始化的时候,我们说了,会将url和handler关联起来,这里代码里的handlerMethod其实就是handler如下图:

    image.png
    image.png

    这个方法返回的变量也是设置成为了handler

    获取内部handler方法getHandlerInternal(request)这部分讲完了,下一部分是方法getHandlerExecutionChain(handler,request),该方法是将相关的拦截器关联到这个handler上,代码如下:

        protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
               HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
               String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
               Iterator var5 = this.adaptedInterceptors.iterator();
    
               while(var5.hasNext()) {
                   HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
                   if (interceptor instanceof MappedInterceptor) {
                       MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
                       if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                           chain.addInterceptor(mappedInterceptor.getInterceptor());
                       }
                   } else {
                       chain.addInterceptor(interceptor);
                   }
               }
    
               return chain;
           }
    

    到这里就完成了一个handlerExcetionChain的创建,下一步是找到相关的handler适配器

        protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
               Iterator var2 = this.handlerAdapters.iterator();
    
               HandlerAdapter ha;
               do {
                   if (!var2.hasNext()) {
                       throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
                   }
    
                   ha = (HandlerAdapter)var2.next();
                   if (this.logger.isTraceEnabled()) {
                       this.logger.trace("Testing handler adapter [" + ha + "]");
                   }
               } while(!ha.supports(handler));
    
               return ha;
           }
    

    这个方法参数是将前面获得的handler执行链的handler,调用handlerExcutionChain.getHandler()的方法返回一个object类型
    进入handlerAdapter.supports(handler)方法


    image.png

    前面的条件是判断handler是否是HandlerMethod的实例,这个地方我们回想一下,在前边获取HandlerMethod的时候,我们将这个类型转换成了Object类型,那么考虑一下,现在这种情况下,handler是否依然是HandlerMethod的实例呢?答案是肯定的,还是HandlerMethod的实例
    测试代码如下:


    image.png

    控制台输出如下:


    image.png
    因为Object是所有类的父类,所以可以子类转换成父类,也可以将父类转换成之前对应的子类。是同一个实例
    好了言归正传,这个方法还有后一个条件,supportsInternal((HandlerMethod)handler)这个方法的代码如下:
    image.png

    这个条件的作用是干嘛的,我现在还不清楚,看方法里的意思是只要是handlerMethod的参数,就会返回一个true

    经过这两个条件的判断,返回一个布尔值到getHandlerAdapter方法中,主要找到一个适配器,就会自动跳出循环,返回handler适配器。

    handler的执行链和handler的适配器都有的情况下,接下来是将handler执行链是否能够成功通过拦截器,只要有一个失败,那么此次请求失败,代码语句是:

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

    然后通过handler适配器进行视图的处理,handlerAdapter.handle(processedRequest, response, mappedHandler.getHandler())
    返回一个ModelAndView的对象

    最后通过
    processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
    方法将mv对象反馈到前端页面。

    总结
    上边说了那么多,如果没看过源码,应该看起来比较吃力,下边将本次的内容进行总结

    1、http请求在tcp层被封装成socket请求发送给tomcat,tomcat用acceptor来接收socket请求,然后根据具体的协议来进行相对应的处理,再交付给线程,tomcat将request打包成requestFacade,经过doFilter重重的过滤器,到达HttpServlet类,调用service(request,response)方法传入请求和响应对象,判断请求类型,然后调用doGet或者doPost方法,接着到FrameworkServlet的processRequest()方法中,调用doService(),再在这个方法中调用doDispatch()方法,至此,完成了请求到分发器的步骤。

    2、请求到达分发器,先判断请求是否是文件上传,文件上传则请求失败,否则通过HandlerMappings迭代器迭代出当前请求对应的handlerMapping,判断是否匹配请求,需要调用handlerMapping.getHandler(request)方法,在这个方法首先调用getHandlerInternal(request)判断根据路径是否能够获取到对应的handler(handlerMethod),如果不能获取到,则设置一个默认的handler,注意在这个方法里边加了一层读锁,接着调用getHandlerExecutionChain(handler,request)方法获取handler的执行链,这个方法实际上是给handler执行链增加拦截器的作用,然后判断一下是否跨域,最后返回匹配好的handler执行链

    3、handler执行链获取成功后,进行第三个重要的步骤,获取handler的适配器,调用getHandlerAdapter()方法,传入handlerExecutionChain.getHandler()作为参数,获取适配器方法的内部使用do...while(条件)的方式迭代出匹配的适配器,判断是否匹配,使用HandlerAdapter.supports(handler)作为条件,这个方法返回的是一个布尔值,如果传入的handler是HandlerMethod的实例,则返回true,跳出循环,匹配成功。

    4、关于ModelAndView这块,这部分内容是根据HandlerExecutionChain和HandlerAdapter二者进行进一步的判定,然后反馈给前端信息,这部分我还没怎么看,现在无法总结。

    相关文章

      网友评论

          本文标题:SpringMVC源码的研究之dispatcherServlet

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