Spring MVC 源码分析

1. DisptcherServlet介绍

在MVC项目搭建的过程中,我们通常会在webapp下的web.xml文件中添加上一个servelet配置,而且通常我们使用的是DispatcherServlet这个类,如下列代码所示:


<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
   <servlet>
      <servlet-name>springmvcservlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
      <init-param>
         <param-name>contextConfigLocationparam-name>
         <param-value>classpath*:springmvc.xmlparam-value>
      init-param>
   servlet>
   <servlet-mapping>
      <servlet-name>springmvcservlet-name>
      <url-pattern>/url-pattern>
   servlet-mapping>
web-app>

那么我们理解一下为什么要配置DispatcherServlet呢? 在传统的Web项目中我们都是直接使用Servlet来创建接口的,为什么在SpringMVC中会使用DispatcherServlet呢?我们看一下DispatcherServlet的继承关系:
Spring MVC 源码分析_第1张图片

从图中可以看出,DispatcherServlet最终还是实现了HttpServlet这个类,这也就实现了Servlet的处理请求的能力。
通过前面的学习我们知道,Servlet处理请求的方法是doGet和doPost方法,那么在SpringMVC中请求的处理流程从继承关系上的实现方式如下图所示:
Spring MVC 源码分析_第2张图片

这样我们就能看到了,最终的请求处理是在DispatcherServlet的doDispatch方法中执行的。后续会对该方法进行详细的解析。

2. 九大组件

2.1 九大组件定义

在DispatcherServlet中定义了九个属性,每⼀个属性都对应⼀种组件


/** MultipartResolver used by this servlet. */
// 多部件解析器
@Nullable
private MultipartResolver multipartResolver;

/** LocaleResolver used by this servlet. */
// 区域化 国际化解析器
@Nullable
private LocaleResolver localeResolver;

/** ThemeResolver used by this servlet. */
// 主题解析器
@Nullable
private ThemeResolver themeResolver;

/** List of HandlerMappings used by this servlet. */
// 处理器映射器组件
@Nullable
private List<HandlerMapping> handlerMappings;

/** List of HandlerAdapters used by this servlet. */
// 处理器适配器组件
@Nullable
private List<HandlerAdapter> handlerAdapters;

/** List of HandlerExceptionResolvers used by this servlet. */
// 异常解析器组件
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;

/** RequestToViewNameTranslator used by this servlet. */
// 默认视图名转换器组件
@Nullable
private RequestToViewNameTranslator viewNameTranslator;

/** FlashMapManager used by this servlet. */
// flash属性管理组件
@Nullable
private FlashMapManager flashMapManager;

/** List of ViewResolvers used by this servlet. */
// 视图解析器
@Nullable
private List<ViewResolver> viewResolvers;

九⼤组件都是定义了接⼝,接⼝其实就是定义了该组件的规范,⽐如ViewResolver、 HandlerAdapter等都是接⼝。

2.2 九大组件初始化

  1. 初始化时机分析:
    在Spring的容器初始化方法中有一个特殊的方法:
    Spring MVC 源码分析_第3张图片

SpringMVC是在Spring框架的基础上进行构建的,在DispatcherServlet类中也实现了这个onRefresh方法,实现九大组件的初始化:

/**
 * This implementation calls {@link #initStrategies}.
 */
@Override
protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}

/**
 * Initialize the strategy objects that this servlet uses.
 * 

May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { // 初始化多部件解析去 initMultipartResolver(context); // 初始化国际化解析器 initLocaleResolver(context); // 初始化主题解析器 initThemeResolver(context); // 初始化处理器映射器 initHandlerMappings(context); // 初始化处理器适配器 initHandlerAdapters(context); // 初始化异常解析器 initHandlerExceptionResolvers(context); // 初始化视图转换器 initRequestToViewNameTranslator(context); // 初始化视图解析器 initViewResolvers(context); // 初始化flash管理器 initFlashMapManager(context); }

下面重点分析initHandlerMappings(context)
2. initHandlerMappings(context)方法解析

/**
 * Initialize the HandlerMappings used by this class.
 * 

If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. // 按照HandlerMapping.class去IOC容器中找到所有的HandlerMapping Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { // 否则在IOC容器按照固定的名称handlerMapping查询 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { // 最后还为空,则按照默认策略生成 // 按照默认方式生成HandlerMapping this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }

如果按照类型和固定的名称在IOC容器找不到对应的组件,则会按照默认的策略去生产HandlerMapping组件,默认的策略是在DispatcherServlet.properties中进行配置的,
Spring MVC 源码分析_第4张图片

下面是DispatcherServlet.properties中的配置:
Spring MVC 源码分析_第5张图片

我们可以看到,这里一共对九大组件中的八个进行了默认的配置,这也说明处理多部件解析器外,其他组件的初始化流程其实跟HandlerMapping处理流程一直唯一没有进行默认配置的就是MultipartResolver-多部件解析器,这也是要注意的地方:

/**
 * Initialize the MultipartResolver used by this class.
 * 

If no bean is defined with the given name in the BeanFactory for this namespace, * no multipart handling is provided. */ private void initMultipartResolver(ApplicationContext context) { try { // 按照默认名称multipartResolver去IOC查询实例 this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isTraceEnabled()) { logger.trace("Detected " + this.multipartResolver); } else if (logger.isDebugEnabled()) { logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName()); } } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isTraceEnabled()) { logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared"); } } }

多部件解析器的初始化必须按照id注册对象(multipartResolver)

3. SpringMVC 处理请求流程源码分析

3.1 重要时机点分析

  1. Handler方法执行时机:
    断点观察调用栈:
    Spring MVC 源码分析_第6张图片
    Spring MVC 源码分析_第7张图片

可以看到是在DispatcherServlet类中的doDispatch方法中执行的请求。
2. 页面渲染时机
Spring MVC 源码分析_第8张图片

3.2 SpringMVC处理请求流程

1.流程概览
SpringMVC处理请求的流程即为org.springframework.web.servlet.DispatcherServlet#doDispatch⽅法的执⾏过程
(1)调⽤getHandler()获取到能够处理当前请求的执⾏链 HandlerExecutionChain(Handler+拦截器)。但是如何去getHandler的?后⾯进⾏分析…
(2)调⽤getHandlerAdapter();获取能够执⾏1)中Handler的适配器,但是如何去getHandlerAdapter的?后⾯进⾏分析…
(3)适配器调⽤Handler执⾏ha.handle(总会返回⼀个ModelAndView对象)
(4)调⽤processDispatchResult()⽅法完成视图渲染跳转

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

         /**
          * 2. 取得处理当前请求的Controller,这⾥也称为Handler,即处理器
          * 这⾥并不是直接返回 Controller,⽽是返回 HandlerExecutionChain 请求处
          * 理链对象
          * 该对象封装了Handler和Inteceptor
          */
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            // 如果handler为空,则返回404
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
         /**
          * 3. 获取处理请求的处理器适配器 HandlerAdapter
          */
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         // 处理 last-modified 请求头
         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.
         /**
          * 4. 实际处理器处理请求,返回结果视图对象
          */
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         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.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      /**
       * 5. 跳转⻚⾯,渲染视图
       */
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      // 最终会调⽤HandlerInterceptor的afterCompletion ⽅法
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      // 最终会调⽤HandlerInterceptor的afterCompletion ⽅法
      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);
         }
      }
   }
}

2.核⼼步骤getHandler⽅法剖析
Spring MVC 源码分析_第9张图片

遍历两个HandlerMapping,试图获取能够处理当前请求的执⾏链
3.核⼼步骤getHandlerAdapter⽅法剖析
Spring MVC 源码分析_第10张图片

遍历各个HandlerAdapter,看哪个Adapter⽀持处理当前Handler

4.核心:ha.handle方法剖析
Spring MVC 源码分析_第11张图片
时机点:
从断点进入:
Spring MVC 源码分析_第12张图片
进入invokeHandlerMethod方法:
Spring MVC 源码分析_第13张图片
进入invokeAndHandle方法:
Spring MVC 源码分析_第14张图片
进入invokeForRequest方法:
Spring MVC 源码分析_第15张图片
5.核⼼步骤processDispatchResult⽅法剖析
关键的时机点: render方法-完成视图渲染
Spring MVC 源码分析_第16张图片
进入render方法中,
Spring MVC 源码分析_第17张图片
解析视图名称,进入到resolveViewName方法中:
Spring MVC 源码分析_第18张图片
每个视图解析都去解析视图,得到一个视图,进入到viewResolver.resolveViewName方法中:
Spring MVC 源码分析_第19张图片
创建视图,视频名称为"success",进入到createView方法中:
Spring MVC 源码分析_第20张图片
调用父类的方法,创建视图,点击进入:
Spring MVC 源码分析_第21张图片
构建视图,点击进入:
Spring MVC 源码分析_第22张图片
到这里,逻辑的视图跟物理视图就对应上了,可以作为一个视图返回了,视图构建成功后,下面的方法进行参数设置:
Spring MVC 源码分析_第23张图片
进入view.render方法:
Spring MVC 源码分析_第24张图片
进入exposeModelAsRequestAttributes方法:
Spring MVC 源码分析_第25张图片

你可能感兴趣的:(SpringMVC,源码,java,spring,mvc)