DispatcherServlet源码分析上

一、总体大流程

代码1

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


   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);


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


      try {
        // 1. 检测是否文件上传请求
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);


         // Determine handler for the current request.
         // 2. 根据请求地址找到哪个类(Controller)能处理请求
         mappedHandler = getHandler(processedRequest);
        // 3. 如果没有找到处理器能处理请求就报404或者抛异常
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }


         // Determine handler adapter for the current request.
        // 4. 拿到能执行这个类的所有方法的适配器,因为有可能类中的方法是继承的,也可能是自定义的,所以会存在不同的适配器去执行相应的方法(类似反射工具)
         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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }


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


         // Actually invoke the handler.
        //    5. 适配器执行目标方法,并返回一个ModelAndView,不管控制器的目标方法返回类型是什么
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


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

         // 如果目标方法返回类型是void,则设置一个默认的视图名   
         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.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
        // 6. 根据方法最终执行完成封装的MAV,转发到页面,而且MAV中的数据可以从请求域中获取
            处理返回结果,包括处理异常、渲染页面、发出完成后通知Interceptor的afterCompletion
      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. 所有请求过来DispatcherServlet收到请求
  2. 调用doDispatch方法进行处理
    - 1. getHandler:根据当前请求地址找到能处理这个请求的目标处理器类(处理器,@Controller)
    根据当前请求在HandlerMapping中找到这个请求的映射信息,获取到目标处理器类
    - 2. getHandlerAdapter(handler):根据当前处理器类获取到能执行这个处理器类方法的适配器
    根据当前处理器类,找到当前处理器的适配器
    - 3. 使用刚才获取到的适配器执行目标方法
    - 4. 目标方法执行后返回一个ModelAndView对象
    - 5. 根据ModelAndView的信息转发到具体的页面,并可以在请求域中取出ModelAndView中的模型数据

二、 getHandler细节

getHandler返回的是HandlerExecutionChain,Handler执行链,包括我们写的Controller和拦截器
1. 看下类定义
在这里插入图片描述

2. 看下方法内部是怎么根据请求地址找到目标处理器的

代码2

* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request 包含请求地址
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping mapping : this.handlerMappings) {
         HandlerExecutionChain handler = mapping.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}

我们可以看到方法内部遍历了一个handlerMappings,它里面保存了每一个处理器能处理哪些请求的映射信息
可以看下List的内容,我们一般给Controller标注解,所以一般用到的是红框里面的HandlerMapping
在这里插入图片描述

可以看下这个这个HandlerMapping里有什么东东:
在这里插入图片描述
可以理解为,我们为Controller的目标方法标了注解@RequestMapping,容器启动的时候就会去查找,找到就把它保存在HandlerMapping的mappingLookup里面

挖我,是不是很清晰明了了,键是地址,值是方法,request中包含的地址过来与这个值匹配,就会执行目标方法。
上去看下代码2的这行

HandlerExecutionChain handler = mapping.getHandler(request);  

3. 那么现在不得不看下HandlerMapping的getHandler方法了

这里要说一下HandlerMapping是一个接口,我们直接看它的实现类 AbstractHandlerMapping中该方法的定义,后面再讲细节

/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
}

wc,看看注释发现了什么,找一个处理器来处理当前的请求,那我们可以自定义一个HandlerMapping吗?等会儿讲,

这里总结一下:getHandler最终拿到的是一个执行器链,这个执行器链里面包含了能处理请求的Handler(@Controller标注的类)和拦截器


三、 getHandlerAdapter细节

 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

我们已经拿到了执行器链,mappedHandler.getHandler()从执行器链里拿到我们之前定义的处理器(也就是@Controller标注的类)
代码1中的注释讲过我们要拿处理器的适配器去执行处理器的目标方法,看下代码
代码3

/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
         if (adapter.supports(handler)) {
            return adapter;
         }
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

看到没,又是一个遍历,看哪个适配器支持处理这个处理器的目标方法,因为每个Adapter的support方法实现都不一样,支持就返回,看看这次handlerAdapters里面有哪些
在这里插入图片描述

再看下HandlerAdapter的实现类会发现只有第一个支持
在这里插入图片描述
看下它的supports方法:

@Override
public final boolean supports(Object handler) {
   return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
//supportsInternal会一直返回true
}

这样就找到了适配器

总结一下: 我们先拿到执行器链中的处理器(handler),遍历所有的适配器看哪个适配器支持这个处理器,支持就返回这个适配器


四、adapter.handle

看下代码1的这行

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

在另一篇中讲adapter的handle细节,现在知道它执行完返回一个ModelAndView,目标方法返回的数据都塞在里面


五、视图渲染

看代码1的这行

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

最终会执行

 /**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable Exception exception) throws Exception {


   boolean errorView = false;


   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }


   // Did the handler return a view to render?
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      if (logger.isTraceEnabled()) {
         logger.trace("No view rendering, null ModelAndView returned.");
      }
   }


   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }


   if (mappedHandler != null) {
      // Exception (if any) is already handled..
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

这个也是DispatcherServlet的内部方法

看一下render

代码4

/**
* Render the given ModelAndView.
* <p>This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
   // Determine locale for request and apply it to the response.
   Locale locale =
         (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
   response.setLocale(locale);


   View view;
   String viewName = mv.getViewName();
   if (viewName != null) {
      // We need to resolve the view name.
        // 1. 选择一个ViewResolver来解析视图
      view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
      if (view == null) {
         throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
               "' in servlet with name '" + getServletName() + "'");
      }
   }
   else {
      // No need to lookup: the ModelAndView object contains the actual View object.
      view = mv.getView();
      if (view == null) {
         throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
               "View object in servlet with name '" + getServletName() + "'");
      }
   }


   // Delegate to the View object for rendering.
   if (logger.isTraceEnabled()) {
      logger.trace("Rendering view [" + view + "] ");
   }
   try {
      if (mv.getStatus() != null) {
         response.setStatus(mv.getStatus().value());
      }
        // 2. 渲染视图
      view.render(mv.getModelInternal(), request, response);
   }
   catch (Exception ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Error rendering view [" + view + "]", ex);
      }
      throw ex;
   }
}

可以看到该方法内部有两个关键点
我们首先来看第一个

view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

看看这个方法的内部

@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
      Locale locale, HttpServletRequest request) throws Exception {


   if (this.viewResolvers != null) {
      for (ViewResolver viewResolver : this.viewResolvers) {
         View view = viewResolver.resolveViewName(viewName, locale);
         if (view != null) {
            return view;
         }
      }
   }
   return null;
}

在这里插入图片描述

看到没,还是遍历一个viewResolver的List看有没有能解析这个视图的视图解析器,有能解析就直接解析后返回,默认是这个InternalResourceViewResolver

返回这个View后接着看代码4的这行

view.render(mv.getModelInternal(), request, response);

它会走InternalResourceView的父类AbstractView的render方法

@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
      HttpServletResponse response) throws Exception {


   if (logger.isDebugEnabled()) {
      logger.debug("View " + formatViewName() +
            ", model " + (model != null ? model : Collections.emptyMap()) +
            (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
   }


   Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
   prepareResponse(request, response);
   renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

在后面调了InternalResourceView的renderMergedOutputModel渲染视图


总结dispatcherServlet会选择一个ViewSolver来解析并渲染视图

六、doDispatch总流程

  1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该请求对应的处理器(Handler)和拦截器(Interceptor)List

  3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter;(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)

  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

  • HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
  • 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
  • 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
  • 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
  1. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

  2. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

  3. ViewResolver 结合Model和View,来渲染视图;

  4. 将渲染结果返回给客户端。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值