SpringMVC 源码解析

SpringMVC 源码解析

图为网上别人的,只可以用来参考,最终真正的运行流程还是要通过自己的源码 debug 来进行理解。

img

完成 Handler - Controller 控制器的方式
  1. 使用注解方式
  2. 实现 Controller 接口
  3. 实现 HttpRequestHeader 接口

实现接口的方法是 BeanName 的方式,他们需要加上 @Component("/uri.html") 注解,并设置 URI 地址,BeanName 方式和 注解的 @GetMapping 和 @PostMapping 一样的效果,只是现在过时了,都用注解编码。

服务器加载时

  服务器加载时会将当前的所有的 controller 请求以 key-value 方式放入 HanderMap 里去,key - uri ,value - controller对象,注解方式 -> 方法对象,beanName 方式 -> 类对象,两种方式不同,在映射器和适配器的方式也不同。

核心方法的入口
  1. FrameworkServlet.doget/dopost/…
  2. FrameworkServlet.processRequest
  3. FrameworkServlet.doService
  4. DispatcherServlet.doService
  5. DispatcherServlet.doDispatch

  DispatcherServlet 继承了 FrameworkServlet,FrameworkServlet 是抽象类,DispatcherServlet 实现了doService 的方法体并且会拦截所有请求, doService 方法中调用了 doDispatch 方法,最终执行的 doDispatch 方法,才完成整个请求到响应的调度与解耦过程。

doDispatch 源码解析

两种处理器,三种适配器

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    // ps:先将方法中要使用的对象进行初始化 
    
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

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


         // Determine handler for the current request. 
         // 确定当前请求的处理程序
         mappedHandler = getHandler(processedRequest);
          
          // ps:结束后会得到一个执行链,mappedHandler(HandlerExecutionChain) 的类中聚合了 Handler 与 拦截器的数组与集合两个成员变量,Handler 是 Object 类型,因为它如果请求的需要执行的代码是 注解方式配置的,则是方法对象,如果是 实现 Controller 或者 HttpRequestHeader 的 BeanName 方式,存储的就是 类对象
          // ps:在 getHandler 执行的方法中,使用策略模式,对 HandlerMappings 集合进行非 null 判断后,将 HandlerMappings 的集合进行 foreach 循环迭代,因为 HandlerMappings 集合里的 HandlerMapping 是接口,所以直接循环进行调用他们的 getHandler 方法完成多态的执行方式,对所有不同的 控制器进行了执行链的装配,并最终返回给 DispatcherServlet 的 doDispatch 的方法中
          
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request. 
         // 确定当前请求的处理程序适配器
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
          
          // ps:这里的控制器适配器将执行链 Handler 取出进行适配的处理,采用了适配器模式,内部会循环规定好的 handlerAdapters 集合里的 handlerAdapter 对象进行迭代,handlerAdapter 也是接口与 HandlerMapping 一样,有不同的适配器对象进行了实现,循环对 Handler 处理器进行类型判断,然后返回一个适配器给 doDispatch 方法中

         // 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

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

         // Actually invoke the handler. 实际调用处理程序
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
          
          //ps:这里对获得到的具体适配器进行方法的调用执行,并将执行链里得到的 Handler 放入其中,最后会调用到我们自己书写的 Handler-Controller 的方法或者对象方法,内部会对我们的方法需要的参数,进行反射机制处理,也就是有个参数处理器来进行操作,判断类型后进行对应的类型转换,或者就是对类的成员属性赋值操作,最终在调用我们方法时候将装配好的参数传给我们的方法中,最终以 mv - ModelAndView 类型对象返回回来到 doDispatch 
          //ps:参数处理器也是一种策略模式,简略了 很多 if...elseif 的操作判断
          //ps:这里结束也就是方法的核心了,后面会根据 ModelAndView 进行视图的解析处理,然后对视图的数据渲染,最终响应给用户显示。

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

         applyDefaultViewName(processedRequest, mv);
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
          //在4。3版本中,我们也在处理处理器方法抛出的错误,
          //使它们可用于@ExceptionHandler方法和其他场景。
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion 
         // 而不是完成后和完成后
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request. 
         // 清除由多部分请求使用的任何资源
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}
流程总结
  1. 首先用户发送过来 URL 的请求,SpringMVC 的核心组件 DisPatchServlet 会拦截到所有的请求,因为在 SpringSSM 中的 web.xml 会配置 Servlet 是会设置 “/” 作为请求的地址,所以会拦截到用户所有的请求,如果是 SpringBoot 则内置了该配置,然后 DisPatchServlet 通过重写了 doService 方法,通过父类的执行调用机制 doget -> processRequest -> doService 得以调用,然后在 doService 还有一个 doDisPatch 方法的调用,也就是真正的核心方法,在这个方法里会调用相关的 HandlerMapping 与 HandlerAdapter 的相关方法的调用执行。

    image-20200830113805415
  2. 在 doDispatch 方法中开始会声明一些后面用到的局部变量,然后先执行 getHandler 的方法,getHandler 方法里其实就是采用的策略模式,先对 HandlerMappings 的集合进行非空判断,然后进行 foreach 循环遍历,这里在程序运行时候其实是会有两个mapping 映射器的实现类存在,一个是注解的方式,一个则是 BeanName 的方式,BeanName的方式是通过实现 Controller 和 实现 HttpRequestHander 接口进行的配置的 Handler, 再迭代时调用他们实现的接口 getHandler 方法,这里内部会对程序初始化时候装配的 HandlerMap 进行 key 的查找,key 为 request 里取出的 URI,值的话可能会是类对象也可能是方法对象,这个取决于你配置这个 URI 采用的注解方式还是BeanName 方法,每个映射器都会进行方法调用尝试获取,如果获取到里面装配好的 执行链不是 null ,则结束循环将得到的 执行链 HandlerExecutionChain 返回给 DisPatchServlet 的 doDispatch 方法中,执行链里依赖了一个 object 类型的 handler 成员属性,和分别是数组和集合的拦截器成员属性。

    image-20200830114143146
  3. 现在 DisPatchServlet 拥有了 HandlerExecutionChain 执行链后,因为执行链里的 handler 是 object 类型,可能是 类对象也可能是方法对象,所以需要合适的适配器对他进行解析具体的调用,这里会调用一个 getHandlerAdapter 方法,方法内部结构和获取映射器 HandlerMapping 的结构差不多,都是先对集合进行非空判断然后循环遍历接口的集合,这里的集合时适配器接口的集合,foeeach 循环的操作内部是将 handler 与各种适配器实现了类型的判断方法进行匹配,如果返回了 true 则将正确的适配器返回给 DisPatchServlet 的 doDispatch 方法中,方便后面的调用。

    image-20200830114436434
  4. 拥有了执行链与适配器后,就可以对具体我们书写的 Controller 也就是 Handler 方法进行调用了,通过适配器 ha.handle(processedRequest, response, mappedHandler.getHandler()) 进行调用,在内部会通过参数处理器对 request 请求体里的字符串参数,进行具体的类型判断和转换,或者对我们的 DOJO 对象进行反射的装配,最后传参的方式传给我们的方法体内进行具体的后端业务操作,最终会以 ModelAndView 的类型对象返回给 DisPatchServlet 的 doDispatch 中。

    // 这是源码中的一段,这里是接口多态的方式进行调用的
    
    // Actually invoke the handler. 实际调用处理程序
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
  5. 在调用 Controller 后,DisPatchServlet 有了最终需要响应的的 ModelAndView 的对象了,后面则是会对这个对象进行 视图解析器 得到一个粗犷的 view 视图,然后通过渲染器对 view 视图进行数据的渲染,并得到最终需要展示给用户的 view 视图,最后响应给用户。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值