Loading

63-SpringMVC源码分析

Quiz:

  1. Spring和SpringMVC整合使用时,会创建一个容器还是两个容器(父子容器?)
  2. DispatcherServlet初始化过程中做了什么?
  3. 请求的执行流程是怎么样的?

SpringMVC 是基于 Servlet 和 Spring 容器设计的 Web 框架。

1. Servlet 回顾

Servlet 接口及其实现类结构:

public interface Servlet {

    public void init(ServletConfig config) throws ServletException;

    public ServletConfig getServletConfig();
    
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
   
    public String getServletInfo();
    
    public void destroy();

}

ServletConfig 是一个和 Servlet 配置相关的接口,在配置 Spring MVC 的 DispatcherServlet 时,会通过 ServletConfig 将配置文件的位置告知 DispatcherServlet。

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
</servlet>

如上,标签内的配置信息最终会被放入 ServletConfig 实现类对象中。DispatcherServlet 通过 ServletConfig 接口中的方法,就能获取到 contextConfigLocation 对应的值。

DispatcherServlet 类图(红色框是 Servlet 中的接口和类,蓝色框中则是 Spring 中的接口和类):

SpringMVC 测试环境(web.xml 的文件在内容在#2有写):

2. 根容器初始化[父容器]

2.1 Web 应用部署初始化过程

参考 Oracle 官方文档,可知 Web 应用部署的相关步骤如下:

可以发现,在 tomcat 下 Web 应用的初始化流程是,先初始化 Listener 接着初始化 Filter 最后初始化 Servlet,当我们清楚认识到 Web 应用部署到容器后的初始化过程后,就可以进一步深入探讨 SpringMVC 的启动过程。

web.xml 配置进行 Spring MVC 启动过程的分析,web.xml 配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<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_4_0.xsd"
         version="4.0">

    <!-- Spring监听器 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- SpringMVC前端控制器 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!-- 该 Servlet 随容器启动实例化 -->
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/test/hello</url-pattern>
    </servlet-mapping>

</web-app>

2.2 ContextLoaderListener 的初始化过程

首先定义了 <context-param> 标签,用于配置一个全局变量,<context-param> 标签的内容读取后会被放进 application 中,作为 Web 应用的全局变量使用,接下来创建 listener 时会使用到这个全局变量,因此,Web 应用在容器中部署后,进行初始化时会先读取这个全局变量,之后再进行上述讲解的初始化启动过程。

接着定义了一个 ContextLoaderListener 类型的 listener。查看 ContextLoaderListener 的类声明源码如下图:

ServletContextListener 接口:

public interface ServletContextListener extends java.util.EventListener {
    
    void contextInitialized(javax.servlet.ServletContextEvent servletContextEvent);

    void contextDestroyed(javax.servlet.ServletContextEvent servletContextEvent);
}

这里采用的是观察者模式,也称为为订阅-发布模式,实现了该接口的 listener 会向发布者进行订阅,当 Web 应用初始化或销毁时会分别调用上述两个方法。

继续看 ContextLoaderListener,该 listener 实现了 ServletContextListener 接口,因此在 Web 应用初始化时会调用该方法,该方法的具体实现如下:

/**
 * Initialize the root web application context.
 */
@Override
public void contextInitialized(ServletContextEvent event) {
  initWebApplicationContext(event.getServletContext());
}

ContextLoaderListener 的 contextInitialized() 方法直接调用了 initWebApplicationContext() 方法,这个方法是继承自 ContextLoader,通过函数名可以知道,该方法是用于初始化 Web 应用上下文,即「IoC 容器」,这里使用的是代理模式,继续查看 ContextLoader 类的 initWebApplicationContext() 方法:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        throw new IllegalStateException(
                "Cannot initialize context because there is already a root application context present - " +
                "check whether you have multiple ContextLoader* definitions in your web.xml!");
    }

    servletContext.log("Initializing Spring root WebApplicationContext");
    Log logger = LogFactory.getLog(ContextLoader.class);
    if (logger.isInfoEnabled()) {
        logger.info("Root WebApplicationContext: initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        // 将上下文存储在本地实例变量中,以确保它在ServletContext关闭时可用。
        // Store context in local instance variable, to guarantee that it is available on ServletContext shutdown.
        if (this.context == null) {
            // => 1.创建web应用上线文环境
            this.context = createWebApplicationContext(servletContext);
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
            // 如果当前上下文环境未激活,那么其只能提供例如设置父上下文、设置上下文id等功能
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent ->
                    // determine parent for root web application context, if any.
                    ApplicationContext parent = loadParentContext(servletContext);
                    cwac.setParent(parent);
                }
                // => 2.配置并刷新当前上下文环境
                configureAndRefreshWebApplicationContext(cwac, servletContext);
            }
        }

        // 将当前上下文环境存储到ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE变量中
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
        if (ccl == ContextLoader.class.getClassLoader()) {
            currentContext = this.context;
        }
        else if (ccl != null) {
            currentContextPerThread.put(ccl, this.context);
        }

        if (logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
        }

        return this.context;
    }
    catch (RuntimeException | Error ex) {
        logger.error("Context initialization failed", ex);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
        throw ex;
    }
}

a. 创建Web应用上线文环境

/**
 * 为当前类加载器实例化根WebApplicationContext,可以是默认上线文加载类或者自定义上线文加载类
 */
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    // 1.确定实例化WebApplicationContext所需的类
    Class<?> contextClass = determineContextClass(sc);
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }
    // 2.实例化得到的WebApplicationContext类
    return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

逻辑很简单,得到一个类,将其实例化。

那么要得到或者明确哪个类呢? 继续看代码:

/**
 * 返回WebApplicationContext(web应用上线文环境)实现类,如果没有自定义默认返回XmlWebApplicationContext类。
 *
 * 两种方式:
 * 1.非自定义
 *     通过ContextLoader类的静态代码块加载ContextLoader.properties配置文件并解析,
 *     该配置文件中的默认类即XmlWebApplicationContext。
 * 2.自定义
 *     通过在web.xml文件中配置context-param节点,并配置param-name为contextClass的自己点,如:
 *      <context-param>
 *          <param-name>contextClass</param-name>
 *          <param-value>org.springframework.web.context.support.MyWebApplicationContext</param-value>
 *      </context-param>
 *
 * Return the WebApplicationContext implementation class to use, either the
 * default XmlWebApplicationContext or a custom context class if specified.
 * @param servletContext current servlet context
 * @return the WebApplicationContext implementation class to use
 */
protected Class<?> determineContextClass(ServletContext servletContext) {
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    // 1.自定义
    if (contextClassName != null) {
        try {
            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", ex);
        }
    }
    // 2.默认
    else {
        // 根据静态代码块的加载这里 contextClassName = XmlWebApplicationContext
        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
        try {
            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", ex);
        }
    }
}

自定义方式注释里已经写的很清晰了,我们来看默认方式,这里涉及到了一个静态变量 defaultStrategies,并在下面的静态代码块中对其进行了初始化操作:

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

private static final Properties defaultStrategies;

static {
    try {
        // 静态代码加载默认策略, 即默认的web应用上下文 [DEFAULT_STRATEGIES_PATH -> ContextLoader.properties]
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
    }
}

这段代码对 ContextLoader.properties 进行了解析,那么 ContextLoader.properties 中存储的内容是什么呢?

# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

很简单,通过上面的操作,我们就可以确定 contextClassName 是 XmlWebApplicationContext,跟我们之前分析的 ApplicationContext 差不多,只是在其基础上又提供了对 Web 的支持。接下来通过 BeanUtils.instantiateClass(contextClass) 将其实例化即可。

initWebApplicationContext() 方法如上注解讲述,主要目的就是创建 root WebApplicationContext 对象即「根 IoC 容器」,其中比较重要的就是,整个 Web 应用如果存在「根IoC容器」,则有且只能有一个,「根 IoC 容器」作为全局变量存储在 ServletContext 即 application对象中。将「根IoC容器」放入到 application 对象之前进行了 IoC 容器的配置和刷新操作,调用了 configureAndRefreshWebApplicationContext() 方法,该方法源码如下。

b. 配置并刷新当前上下文环境

/**
 * 配置并刷新当前web应用上下文
 */
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    /**
     * 1.配置应用程序上下文id
     * 如果当前应用程序上下文id仍然设置为其原始默认值,则尝试为其设置自定义上下文id,如果有的话。
     * 在web.xml中配置:
     * <context-param>
     *      <param-name>contextId</param-name>
     *      <param-value>jack-2019-01-02</param-value>
     *  </context-param>
     */
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
        if (idParam != null) {
            wac.setId(idParam);
        }
        // 无自定义id则为其生成默认id
        else {
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    wac.setServletContext(sc);

    /**
     * 2.设置配置文件路径
     * >>> applicationContext.xml <<<
     * <context-param>
     *      <param-name>contextConfigLocation</param-name>
     *      <param-value>classpath:spring-context.xml</param-value>
     *  </context-param>
     */
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    if (configLocationParam != null) {
        wac.setConfigLocation(configLocationParam);
    }

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    // 3.创建ConfigurableEnvironment并配置初始化参数
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    }

    // 4.自定义配置上下文环境
    customizeContext(sc, wac);

    // 5.刷新上下文环境
    wac.refresh();
}

比较重要的就是获取到了 web.xml 中的 <context-param> 标签配置的全局变量 contextConfigLocation,并最后一行调用了 refresh() 方法,ConfigurableWebApplicationContext 是一个接口,通过对常用实现类 ClassPathXmlApplicationContext 逐层查找后可以找到一个抽象类 AbstractApplicationContext 实现了 refresh() 方法。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1、准备刷新上下文环境
        prepareRefresh();
        // 2、读取xml并初始化BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 3、填充BeanFactory功能
        prepareBeanFactory(beanFactory);
        try {
            // 4、子类覆盖方法额外处理(空方法)
            postProcessBeanFactory(beanFactory);
            // 5、调用BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);
            // 6、注册BeanPostProcessors
            registerBeanPostProcessors(beanFactory);
            // 7、初始化Message资源
            initMessageSource();
            // 8、初始事件广播器
            initApplicationEventMulticaster();
            // 9、留给子类初始化其他Bean(空的模板方法)
            onRefresh();
            // 10、注册事件监听器
            registerListeners();
            // 11、初始化其他的单例Bean(非延迟加载的)
            finishBeanFactoryInitialization(beanFactory);
            // 12、完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知
            finishRefresh();
        }
        catch (BeansException ex) {
            // 13、销毁已经创建的Bean
            destroyBeans();
            // 14、重置容器激活标签
            cancelRefresh(ex);
            throw ex;
        }
        finally {
            resetCommonCaches();
        }
    }
}

该方法主要用于创建并初始化 contextConfigLocation 类配置的 xml 文件中的 Bean,因此,如果我们在配置 Bean 时出错,在 Web 应用启动时就会抛出异常,而不是等到运行时才抛出异常。

整个 ContextLoaderListener 类的启动过程到此就结束了,可以发现,创建 ContextLoaderListener 是比较核心的一个步骤,主要工作就是为了创建「根 IoC 容器」并使用特定的 key 将其放入到 application 对象中,供整个 Web 应用使用,由于在 ContextLoaderListener 类中构造的「根 IoC 容器」配置的 Bean 是全局共享的,因此在 <context-param> 标识的 contextConfigLocation 的 xml 配置文件一般包括:数据库 DataSource、DAO 层、Service 层、事务等相关 Bean。

3. DispatcherServlet 初始化[子容器&九组件]

3.1 HttpServletBean 初始化

Web 应用启动的最后一个步骤就是创建和初始化相关 Servlet,我们配置了 DispatcherServlet 类前端控制器,前端控制器作为中央控制器是整个 Web 应用的核心,用于获取分发用户请求并返回响应。

通过类图可以看出 DispatcherServlet 类的间接父类实现了 Servlet 接口,因此其本质上依旧是一个 Servlet。

由于 DispatcherServlet 类的本质是 Servlet,所以在 Web 应用部署到容器后进行 Servlet 初始化时会调用相关的 init(ServletConfig) 方法,因此 DispatchServlet 类的初始化过程也由该方法开始(DispatcherServelt 没有 init 方法,会走到父类 HttpServletBean 的 init 方法):

/**
 * DispatcherServlet 初始化入口
 */
@Override
public final void init() throws ServletException {

    /**
     * Set bean properties from init parameters.
     * 1.加载初始化参数,这里会解析init-param列表
     * <servlet>
     *      <servlet-name>example</servlet-name>
     *      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     *      <init-param>
     *          <param-name>name</param-name>
     *          <param-value>jack</param-value>
     *      </init-param>
     *      <load-on-startup>1</load-on-startup>
     *  </servlet>
     */
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
          // 将当前的这个 Servlet 类转化为一个 BeanWrapper,从而能够以 Spring 的方法来对 init-param 的值进行注入
          BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
          ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
          // 注册自定义属性编辑器,一旦遇到 Resource 类型的属性将会使用 ResourceEditor 进行解析
          bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
          // 空实现,留给子类覆盖
          initBeanWrapper(bw);
          // 属性注入
          bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }

    // => 2.留给子类覆盖的模板方法
    initServletBean();
}

该方法最主要的作用就是初始化 init-param,如果我们没有配置任何 init-param,那么该方法不会执行任何操作。从这里我们没有拿到有用的信息,但是在该方法结尾有 initServletBean(),这是一个模板方法,可以由子类来实现,那么接下来我们就去看其子类 FrameworkServlet 中的 initServletBean()。

3.2 FrameworkServlet 初始化

继续查看 initServletBean()。父类 FrameworkServlet 覆盖了 HttpServletBean 中的 initServletBean 函数,如下:

protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
    if (logger.isInfoEnabled()) {
        logger.info("Initializing Servlet '" + getServletName() + "'");
    }
    long startTime = System.currentTimeMillis();

    try {
        // => 为当前servlet初始化web应用上下文
        this.webApplicationContext = initWebApplicationContext();
        // 空的模板方法
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (logger.isDebugEnabled()) {
        String value = this.enableLoggingRequestDetails ?
                "shown which may lead to unsafe logging of potentially sensitive data" :
                "masked to prevent unsafe logging of potentially sensitive data";
        logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                "': request parameters and headers will be " + value);
    }

    if (logger.isInfoEnabled()) {
        logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
    }
}

initWebApplicationContext:

protected WebApplicationContext initWebApplicationContext() {
    // 获取rootContext,该Context就是上面通过ContextLoaderListener创建的XmlWebApplicationContext
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    // 如果当前webApplicationContext不为null,则为其设置父容器
    if (this.webApplicationContext != null) {
        // A context instance was injected at construction time -> use it
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // 如果当前Servelt存在一个WebApplicationContext即子IoC容器
                    // 并且上文获取的根IoC容器存在,则将根IoC容器作为子IoC容器的父容器
                    cwac.setParent(rootContext);
                }
                // 配置并刷新当前的子IoC容器,用于构建相关Bean
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    // 未能通过构造函数注入,则尝试去ServletContext容器中查找有无WebApplicationContext
    if (wac == null) {
        // 如果当前Servlet不存在一个子IoC容器则去查找一下
        wac = findWebApplicationContext();
    }
    // => 以上均无WebApplicationContext,则创建一个新的WebApplicationContext
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);
    }

    // 刷新上下文容器,空的模板方法,留给子类实现
    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}

通过函数名不难发现,该方法的主要作用同样是创建一个 WebApplicationContext 对象,即 IoC 容器,不过前面讲过每个 Web 应用最多只能存在一个根 IoC 容器,这里创建的则是特定 Servlet 拥有的子 IoC 容器

为什么需要多个 IOC 容器呢?

答:父子容器类似于类的继承关系,子类可以访问父类中的成员变量,而父类不可访问子类的成员变量,同样的,子容器可以访问父容器中定义的 Bean,但父容器无法访问子容器定义的 Bean。

根 IoC 容器做为全局共享的 IoC 容器放入 Web 应用需要共享的 Bean,而子 IoC 容器根据需求的不同,放入不同的 Bean,这样能够做到隔离,保证系统的安全性。

DispatcherServlet 类的子 IoC 容器创建过程,如果当前 Servlet 存在一个 IoC 容器则为其设置根 IoC 容器作为其父类,并配置刷新该容器,用于构造其定义的 Bean,这里的方法与前文讲述的根 IoC 容器类似,同样会读取用户在 web.xml 中配置的中的值,用于查找相关的 xml 配置文件用于构造定义的 Bean,这里不再赘述了。如果当前 Servlet 不存在一个子 IoC 容器就去查找一个,如果仍然没有查找到则调用 createWebApplicationContext() 方法去创建一个,查看该方法的源码如下图所示:

protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
}

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
  // 获取 servlet 的初始化参数 contextClass,如果没有配置默认为 XMLWebApplicationContext.Class
  Class<?> contextClass = getContextClass();

  // 校验必须是ConfigurableWebApplicationContext的子类
  if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
    throw new ApplicationContextException(
      "Fatal initialization error in servlet with name '" + getServletName() +
      "': custom WebApplicationContext class [" + contextClass.getName() +
      "] is not of type ConfigurableWebApplicationContext");
  }

  // 通过反射方式实例化 contextClass
  ConfigurableWebApplicationContext wac =
    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

  // 设置好父容器、Enviroment等等
  wac.setEnvironment(getEnvironment());
  wac.setParent(parent);

  // 获取 contextConfigLocation 属性,配置在 servlet 初始化参数中
  String configLocation = getContextConfigLocation();
  if (configLocation != null) {
    wac.setConfigLocation(configLocation);
  }

  // => 这个是重点!如完善、初始化、刷新容器
  configureAndRefreshWebApplicationContext(wac);

  return wac;
}

进入 configureAndRefreshWebApplicationContext():

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
  if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
    // The application context id is still set to its original default value
    // -> assign a more useful id based on available information
    if (this.contextId != null) {
      wac.setId(this.contextId);
    }
    else {
      // 默认的id  这里面和contextpath有关了
      wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                          ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
    }
  }

  // 关联到了Namespace/Servlet等等
  wac.setServletContext(getServletContext());
  wac.setServletConfig(getServletConfig());
  wac.setNamespace(getNamespace());

  // 添加了一个容器监听器 此监听器SourceFilteringListener在后面还会碰到
  wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

  // The wac environment's #initPropertySources will be called in any case when the context
  // is refreshed; do it eagerly here to ensure servlet property sources are in place for
  // use in any post-processing or initialization that occurs below prior to #refresh
  ConfigurableEnvironment env = wac.getEnvironment();
  if (env instanceof ConfigurableWebEnvironment) {
    ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
  }

  // 留给子类,可以复写此方法,做一些初始化时候自己的实行
  postProcessWebApplicationContext(wac);
  // 执行一些初始化类们(一般也是用不着)
  applyInitializers(wac);
  // => 加载配置文件及整合 parent 到 wac(该方法就是 Spring 启动入口的那个 refresh 方法~)
  // e.g. 回忆该方法,其中一步是loadBeanDefinitions=>解析spring-mvc.xml文件=>解析<mvc:annotation-driven>注解
  wac.refresh();
}

该方法用于创建一个「子 IoC 容器」并将「根 IoC 容器」做为其父容器,接着进行配置和刷新操作用于构造相关的 Bean。至此,「根 IoC 容器」以及相关 Servlet 的「子 IoC 容器」已经配置完成,子容器中管理的 Bean 一般只被该 Servlet 使用,因此,其中管理的 Bean 一般是“局部”的,如 SpringMVC 中需要的各种重要组件,包括 Controller、Interceptor、Converter、ExceptionResolver 等。相关关系如下图所示:

3.3 DispatcherServlet 初始化

了解 DispatcherServlet 之前,先回顾一下 DispatcherServlet 的内置组件及其作用。

DispatcherServlet#onRefresh();

当 IoC 子容器构造完成后调用了onRefresh() 方法,该方法的调用与 initServletBean() 方法的调用相同,由父类调用但具体实现由子类覆盖,调用 onRefresh() 方法时将前文创建的 IoC 子容器作为参数传入,查看 DispatcherServletBean 类的 onRefresh() 方法:

@Override
protected void onRefresh(ApplicationContext context) {
  initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
  // 1.初始化 MultipartResolver
  initMultipartResolver(context);
  // 2.初始化 LocaleResolver
  initLocaleResolver(context);
  // 3.初始化 ThemeResolver
  initThemeResolver(context);
  // 4.初始化 HandlerMappings
  initHandlerMappings(context);
  // 5.初始化 HandlerAdapters
  initHandlerAdapters(context);
  // 6.初始化 HandlerExceptionResolver
  initHandlerExceptionResolvers(context);
  // 7.初始化 RequestToViewNameTranslator
  initRequestToViewNameTranslator(context);
  // 8.初始化 ViewResolvers
  initViewResolvers(context);
  // 9.初始化 FlashMapManager
  initFlashMapManager(context);
}

onRefresh() 方法直接调用了 initStrategies() 方法,源码如上,通过函数名可以判断,该方法用于初始化创建 multipartResovler 来支持图片等文件的上传、本地化解析器、主题解析器、HandlerMapping 处理器映射器、HandlerAdapter 处理器适配器、异常解析器、视图解析器、flashMap 管理器等,这些组件都是 SpringMVC 开发中的重要组件,相关组件的初始化创建过程均在此完成。

着重看下 initHandlerMappings:

  • Handler : 绑定了 @RequestMapping 和 @Controller 的类;
  • HandlerMethod:就是 Handler 下某个绑定 @RequestMapping 注解的方法(@GetMapping、@PostMapping 等都绑定的注解 @RequestMapping,SpringMVC 在做注解解析处理生成代理对象等的时候,会做值的合并等处理,所以最终都是用 @RequestMapping 来计算,所以 @Controller 和 @RestController 的处理等同)。
/**
 * Initialize the HandlerMappings used by this class.
 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
 * we default to BeanNameUrlHandlerMapping.
 * 实例化HandlerMappings,如果没有自定义HandlerMappings,则默认使用BeanNameUrlHandlerMapping
 */
private void initHandlerMappings(ApplicationContext context) {
	// 初始化记录 HandlerMapping 对象的属性变量为null
	this.handlerMappings = null;

	// 1.根据属性detectAllHandlerMappings决定是检测所有的 HandlerMapping 对象,还是使用指定名称的 HandlerMapping 对象
	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		// 从容器及其祖先容器查找所有类型为 HandlerMapping 的 HandlerMapping 对象,记录到   handlerMappings 并排序
		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.
			// 关于这里的排序可以参考 WebMvcConfigurationSupport 类中对各种 HandlerMapping bean进行定义时所使用的order属性
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
  // 2.否则只获取当前上下文自定义配置的handlerMapping
	else {
		try {
			// 获取名称为  handlerMapping 的 HandlerMapping bean 并记录到 handlerMappings
			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.
	// 3.上述两步都未能获取到handlerMapping,则使用默认的handlerMapping,
	if (this.handlerMappings == null) {
		// 如果上面步骤从容器获取 HandlerMapping 失败,则使用缺省策略创建 HandlerMapping 对象记录到
		// handlerMappings
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
					"': using default strategies from DispatcherServlet.properties");
		}
	}
}

(1)RequestMappingHandlerMapping

这个就是我们常见的基于注解的映射方式,例如:

@Controller
@RequestMapping("/testA")
public class MappingTest1 {
    @ResponseBody
    @RequestMapping("/index")
    public String index(){
        return "RequestMappingHandlerMapping test!";
    }
}

Springboot 在初始化 RequestMappingHandlerMapping 时,会扫描容器中的 bean,判断它上面是否存在 @Controller 或 @RequestMapping 两种注解,通过上面的方法,判断该 bean 是否是一个 handler,如果是,则会将其注册到 RequestMappingHandlerMapping,用来处理和它匹配的请求。

(2)SimpleUrlHandlerMapping

这种方式直接通过简单的 url 匹配的方式将其映射到一个处理器。首先像容器注册一个自定义的 SimpleUrlHandlerMapping。

@Configuration
public class MyConfig extends SimpleUrlHandlerMapping{

    @Bean
    public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
        SimpleUrlHandlerMapping simpleUrlHandlerMapping = new SimpleUrlHandlerMapping();
        Properties properties = new Properties();
        properties.setProperty("simpleUrl","mappingTest2");
        simpleUrlHandlerMapping.setMappings(properties);
        
        // 设置该handlermapping的优先级为1,否则会被默认的覆盖,导致访问无效
        simpleUrlHandlerMapping.setOrder(1);
        
        return simpleUrlHandlerMapping;
    }
}

定义一个名称为 mappingTest2 的 bean,并实现 org.springframework.web.servlet.mvc.Controller 接口。

@Component("mappingTest2")
public class MappingTest2 implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.getWriter().write("SimpleUrlHandlerMapping test!");
        return null;
    }
}

在这个例子中,我们访问 localhost/simpleUrl 就会直接进入容器中名称为 mappingTest2 的 bean 的 handleRequest 方法。

(3)BeanNameUrlHandlerMapping

这个最简单,直接以 bean 的名称作为访问路径,但有个硬性条件就是 bean 的名称必须以 / 开始。

@Component("/mappingTest3")
public class MappingTest3 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.getWriter().write("BeanNameUrlHandlerMapping test!");
        return null;
    }
}

【小结】

  1. HttpServletBean 主要做一些初始化的工作,将 web.xml 中配置的参数设置到 Servlet 中。比如 servlet 标签的子标签 init-param 标签中配置的参数。
  2. FrameworkServlet 将 Servlet 与 Spring 容器上下文关联。其实也就是初始化 FrameworkServlet 的属性 webApplicationContext,这个属性代表 SpringMVC 上下文,它有个父类上下文,即 web.xml 中配置的 ContextLoaderListener 监听器初始化的容器上下文。
  3. DispatcherServlet 初始化各个功能的实现类。比如异常处理、视图处理、请求映射处理等。

DispatcherServlet准备HandlerMapping的流程如下:

  • 当 detectAllHandlerMappings 为 true 时,从容器(以及祖先容器)获取所有类型为 HandlerMapping 的 bean 组件,记录到 handlerMappings 并排序;
  • 当 detectAllHandlerMappings 为 false 时,从容器(以及祖先容器)获取名称为 handlerMapping 的 bean 组件,记录到 handlerMappings,这种情况下 handlerMappings 中最多有一个元素;
  • 如果上面步骤结束时 handlerMappings 为空则创建缺省 HandlerMapping 对象记录到 handlerMappings。

4. mvc:annotation-driven 标签解析

mvc:annotation-driven 标签默认会开启 SpringMVC 的注解驱动模式,默认注册一个 RequestMappingHandlerMapping、一个 RequestMappingHandlerAdapter、一个 ExceptionHandlerExceptionResolver 以支持对使用了 @RequestMapping、@ExceptionHandler 及其他注解的控制器方法的请求处理。

@Override
public void refresh() throws BeansException, IllegalStateException {

  // ...
  
  /*
     2、初始化新BeanFactory
      (1)如果存在旧 BeanFactory,则销毁
      (2)创建新的 BeanFactory(DefaluListbaleBeanFactory)
      (3)解析xml/加载 Bean 定义、注册 Bean定义到beanFactory(不初始化)
      (4)返回新的 BeanFactory(DefaluListbaleBeanFactory)
   */
  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

  // ...
  
  // 11、实例化所有剩余的(非惰性初始化)单例
  // (1)初始化所有的 singleton beans,反射生成对象/填充
  // (2)调用Bean的前置处理器和后置处理器
  finishBeanFactoryInitialization(beanFactory);
  
  // 12、结束refresh操作: 发布事件 & 清除上下文环境
  finishRefresh();

}

关于定位自定义标签解析的过程,IoC 中已经说明过,这里直接打开 AnnotationDrivenBeanDefinitionParser 类并定位到其 parse 方法。

/** 解析 mvc:annotation-driven 标签 */
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    Object source = parserContext.extractSource(element);
    XmlReaderContext readerContext = parserContext.getReaderContext();

    CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
    parserContext.pushContainingComponent(compDefinition);

    /**
     * 获取协商内容视图配置
     */
    RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);

    /**
     * [1] 创建RequestMappingHandlerMapping的RootBeanDefinition
     *   > 从这里也可以看出,开启mvc:annotation-driven标签后,将会默认注册RequestMappingHandlerMapping作为默认的HandlerMapping
     */
    RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
    handlerMappingDef.setSource(source);
    handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    handlerMappingDef.getPropertyValues().add("order", 0);
    handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);

    // 是否开启矩阵变量
    if (element.hasAttribute("enable-matrix-variables")) {
        Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
        handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
    }

    // 解析path-matching路径匹配标签
    configurePathMatchingProperties(handlerMappingDef, element, parserContext);
    readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);

    // 解析cors跨域标签
    RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
    handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);

    // 解析conversion-service数据转换、格式化标签
    RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
    // 解析validator标签
    RuntimeBeanReference validator = getValidator(element, source, parserContext);
    // 解析message-codes-resolver标签
    RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);

    /**
     * [2] 创建ConfigurableWebBindingInitializer的RootBeanDefinition对象
     * > 并将上一步解析的conversionService、validator、messageCodesResolver作为属性注入到该对象中
     */
    RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
    bindingDef.setSource(source);
    bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    bindingDef.getPropertyValues().add("conversionService", conversionService);
    bindingDef.getPropertyValues().add("validator", validator);
    bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);

    // 解析message-converters标签
    ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
    // 解析argument-resolvers标签
    ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
    // 解析return-value-handlers标签
    ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);
    // 解析async-support标签
    String asyncTimeout = getAsyncTimeout(element);
    // 解析async-support的task-executor子标签
    RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
    // 解析async-support的callable-interceptors子标签
    ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
    // 解析async-support的deferred-result-interceptors子标签
    ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);

    /**
     * [3] 创建RequestMappingHandlerAdapter的RootBeanDefinition
     *   > 从这里也可以看出,开启mvc:annotation-driven标签后,将会默认注册RequestMappingHandlerAdapter
     *     作为默认的HandlerAdapter,并将上面解析的内容绑定到该HandlerAdapter中。
     */
    RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
    handlerAdapterDef.setSource(source);
    handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
    handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
    handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
    addRequestBodyAdvice(handlerAdapterDef);
    addResponseBodyAdvice(handlerAdapterDef);

    if (element.hasAttribute("ignore-default-model-on-redirect")) {
        Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
        handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
    }
    if (argumentResolvers != null) {
        handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
    }
    if (returnValueHandlers != null) {
        handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
    }
    if (asyncTimeout != null) {
        handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
    }
    if (asyncExecutor != null) {
        handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
    }

    handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
    handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
    readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME , handlerAdapterDef);

    /**
     * [4] 创建CompositeUriComponentsContributorFactoryBean的RootBeanDefinition
     *   > CompositeUriComponentsContributorFactoryBean是一个工厂bean,可以用来获取
     *     RequestMappingHandlerAdapter中的HandlerMethodArgumentResolver配置。
     */
    RootBeanDefinition uriContributorDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
    uriContributorDef.setSource(source);
    uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
    uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
    String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
    readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);

    /**
     * [5] 创建ConversionServiceExposingInterceptor的RootBeanDefinition
     *   > 主要用来解析spring:eval标签
     */
    RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
    csInterceptorDef.setSource(source);
    csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
    RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
    mappedInterceptorDef.setSource(source);
    mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
    mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
    String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);

    /**
     * [6] 创建ExceptionHandlerExceptionResolver的RootBeanDefinition
     */
    RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
    methodExceptionResolver.setSource(source);
    methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
    methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
    methodExceptionResolver.getPropertyValues().add("order", 0);
    addResponseBodyAdvice(methodExceptionResolver);
    if (argumentResolvers != null) {
        methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
    }
    if (returnValueHandlers != null) {
        methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
    }
    String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);

    /**
     * [7] 创建ResponseStatusExceptionResolver的RootBeanDefinition
     */
    RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
    statusExceptionResolver.setSource(source);
    statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    statusExceptionResolver.getPropertyValues().add("order", 1);
    String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);

    /**
     * [8] 创建DefaultHandlerExceptionResolver的RootBeanDefinition
     *   > 该类是HandlerExceptionResolver的默认实现,可以解析http异常并将相应的http状态码返回,如404
     */
    RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
    defaultExceptionResolver.setSource(source);
    defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    defaultExceptionResolver.getPropertyValues().add("order", 2);
    String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);

    /**
     * 将上面创建的RootBeanDefinition以组件形式纳入SpringIOC容器
     */
    parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
    parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
    parserContext.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));
    parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
    parserContext.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));
    parserContext.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));
    parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));

    /**
     * 注册默认组件
     */
    MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

    parserContext.popAndRegisterContainingComponent();

    return null;
}

那么接下来我们需要总结一下,如果 mvc:annotation-driven 没有配置任何子标签的话,Spring 会如何处理呢?

RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
RootBeanDefinition uriContributorDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);

可以看到即使不做任何子标签的配置,SpringMVC 默认也会创建上述 9 个内部 bean 的实例。

下面来看下这俩个 RootBeanDefinition 走 Bean 实例化生命周期那一套,其中一步就是 InitializingBean#afterPropertiesSet。

4.1 RequestMappingHandlerMapping

HandlerMapping 在 SpringMVC 扮演着相当重要的角色,它可以为 HTTP 请求找到对应的 Controller 控制器。

HandlerMapping 是一个接口,其中包含一个 getHandler 方法,能够通过该方法获得与 HTTP 请求对应的 handlerExecutionChain,而这个 handlerExecutionChain 对象中持有 handler 和 interceptorList,以及和设置拦截器相关的方法。可以判断是通过这些配置的拦截器对 handler 对象提供的功能进行了一波增强。

RequestMappingHandlerMapping 类继承结构:

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
																				  implements MatchableHandlerMapping, EmbeddedValueResolverAware {}
/* |extends| */
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {}
/* |extends| */
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {}

可以看到 RequestMappingHandlerMapping 实现了 InitializingBean。AbstractHandlerMethodMapping 中当 bean 被注入到容器后会执行一系列的初始化过程。

@Override
public void afterPropertiesSet() {
  // 创建 BuilderConfiguration
  this.config = new RequestMappingInfo.BuilderConfiguration();
  this.config.setUrlPathHelper(getUrlPathHelper());
  this.config.setPathMatcher(getPathMatcher());
  this.config.setSuffixPatternMatch(useSuffixPatternMatch());
  this.config.setTrailingSlashMatch(useTrailingSlashMatch());
  this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
  this.config.setContentNegotiationManager(getContentNegotiationManager());
  // => [0]
  super.afterPropertiesSet();
}

进行 HandlerMethod 的注册操作,简单来说就是从 SpringMVC 的容器中获取所有的 beanName,注册 url 和实现方法 HandlerMethod 的对应关系。

/* AbstractHandlerMethodMapping.java */

@Override
public void afterPropertiesSet() {
  initHandlerMethods();
}

// -[0]-
protected void initHandlerMethods() {
  // 获取所有的 BeanNames
  for (String beanName : getCandidateBeanNames()) {
    // 判断不是已 scopedTarget 开头
    if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
      // => [1]
      processCandidateBean(beanName);
    }
  }
  handlerMethodsInitialized(getHandlerMethods());
}

// -[1]-
protected void processCandidateBean(String beanName) {
  Class<?> beanType = null;
  try {
    // 获取具体的类型
    beanType = obtainApplicationContext().getType(beanName);
  } catch (Throwable ex) {
    // 一个无法解析的bean类型,可能来自一个lazy bean,此时忽略它
    if (logger.isTraceEnabled()) {
      logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
    }
  }
  // 不是 null 并且 类型是存在 @Controller/@RequestMapping 注解
  if (beanType != null && isHandler(beanType)) {
    // => [2]
    detectHandlerMethods(beanName);
  }
}

@Override
protected boolean isHandler(Class<?> beanType) {
  // 存在 Controller / RequestMapping 注解
  return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class)
            || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

// -[2]-
protected void detectHandlerMethods(Object handler) {
	// 如果传递是 String 则获取其类型 ,如果是 class 则直接返回
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
		// 对类型再次进行处理,主要是针对cglib
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		// 遍历方法,对注解中的信息进行处理,得到RequestMappingInfo对象,得到methods集
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						// => [5] 里面主要是获取方法和类上的 @RequestMapping 将其合并
						// 如果没有的话则会返回 null,而由于是 Lambda 这里主要是制订过滤规则。返回null则selectMethods不会将其放入到Map中
						return getMappingForMethod(method, userType);
					} catch (Throwable ex) {
						throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex);
					}
				});
		if (logger.isTraceEnabled()) {
			logger.trace(formatMappings(userType, methods));
		}
		// 遍历methods[Method,{path}]
		methods.forEach((method, mapping) -> {
			// 对方法的可访问性进行校验,如private,static,SpringProxy,获取最终请求路径
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			// => [4] 注册到全局的MappingRegistry实例里!
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

// -[4]-
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    // registerHandlerMethod 的注册操作是将 beanName、Method 及创建的 RequestMappingInfo 之间的关系保存
		this.mappingRegistry.register(mapping, handler, method);
}


/* RequestMappingHandlerMapping.java */
// -[5]-
@Override
@Nullable
/* getMappingForMethod方法是在子类RequestMappingHandlerMapping中实现的,具体实现就是创建一个RequestMappingInfo */
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
    // => [6]
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
		String prefix = getPathPrefix(handlerType);
		if (prefix != null) {
			info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
		}
	}
	return info;
}

// -[6]-
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class
																		? getCustomTypeCondition((Class<?>) element)
                                    : getCustomMethodCondition((Method) element));
  // => [7]
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

// -[7]-
protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

[4] 实现了将 url 和 HandlerMethod 的对应关系注册到 mappingRegistry 中。MappingRegistry 中的注册实现如下,并且 MappingRegistry 定义了几个 map 结构,用来存储注册信息。

/**
 * A registry that maintains all mappings to handler methods, exposing methods
 * to perform lookups and providing concurrent access.
 */
class MappingRegistry {

  private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

  private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

  private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

  private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

  private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

  private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

  /** 完成 beanName、HandlerMethod 及 RequestMappingInfo 之间的对应关系注册 */
  public void register(T mapping, Object handler, Method method) {
    // Assert that the handler method is not a suspending one.
    if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
      Class<?>[] parameterTypes = method.getParameterTypes();
      if ((parameterTypes.length > 0) && 
              "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
        throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
      }
    }
    this.readWriteLock.writeLock().lock();
    try {
      // 处理方法的对象
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);
      // 判断映射的唯一性
      validateMethodMapping(handlerMethod, mapping);
      // 将mapping信息和控制器方法对应
      this.mappingLookup.put(mapping, handlerMethod);

      // 将path与处理器映射(一个方法可能可以处理多个url)
      List<String> directUrls = getDirectUrls(mapping);
      for (String url : directUrls) {
        this.urlLookup.add(url, mapping);
      }

      // 控制器名的大写英文缩写#方法名
      String name = null;
      if (getNamingStrategy() != null) {
        name = getNamingStrategy().getName(handlerMethod, mapping);
        addMappingName(name, handlerMethod);
      }

      // 跨域请求相关配置
      CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
      if (corsConfig != null) {
        this.corsLookup.put(handlerMethod, corsConfig);
      }

      // 将所有配置统一注册到registry中
      this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
    } finally {
      this.readWriteLock.writeLock().unlock();
    }
  }
}

4.2 RequestMappingHandlerAdapter

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
																						implements BeanFactoryAware, InitializingBean {
  
	@Override
	public void afterPropertiesSet() {
		// Do this first, it may add ResponseBody advice beans
		initControllerAdviceCache();

		if (this.argumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

}

(1)ArgumentResolver

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
	List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

	// Annotation-based argument resolution
	resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
	resolvers.add(new RequestParamMapMethodArgumentResolver());
	resolvers.add(new PathVariableMethodArgumentResolver());
	resolvers.add(new PathVariableMapMethodArgumentResolver());
	resolvers.add(new MatrixVariableMethodArgumentResolver());
	resolvers.add(new MatrixVariableMapMethodArgumentResolver());
	resolvers.add(new ServletModelAttributeMethodProcessor(false));
	resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new RequestHeaderMapMethodArgumentResolver());
	resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new SessionAttributeMethodArgumentResolver());
	resolvers.add(new RequestAttributeMethodArgumentResolver());

	// Type-based argument resolution
	resolvers.add(new ServletRequestMethodArgumentResolver());
	resolvers.add(new ServletResponseMethodArgumentResolver());
	resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
	resolvers.add(new RedirectAttributesMethodArgumentResolver());
	resolvers.add(new ModelMethodProcessor());
	resolvers.add(new MapMethodProcessor());
	resolvers.add(new ErrorsMethodArgumentResolver());
	resolvers.add(new SessionStatusMethodArgumentResolver());
	resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

	// Custom arguments
	if (getCustomArgumentResolvers() != null) {
		resolvers.addAll(getCustomArgumentResolvers());
	}

	// Catch-all
	resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
	resolvers.add(new ServletModelAttributeMethodProcessor(true));

	return resolvers;
}

(2)BinderArgumentResolver

private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
  List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);

  // Annotation-based argument resolution
  resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
  resolvers.add(new RequestParamMapMethodArgumentResolver());
  resolvers.add(new PathVariableMethodArgumentResolver());
  resolvers.add(new PathVariableMapMethodArgumentResolver());
  resolvers.add(new MatrixVariableMethodArgumentResolver());
  resolvers.add(new MatrixVariableMapMethodArgumentResolver());
  resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
  resolvers.add(new SessionAttributeMethodArgumentResolver());
  resolvers.add(new RequestAttributeMethodArgumentResolver());

  // Type-based argument resolution
  resolvers.add(new ServletRequestMethodArgumentResolver());
  resolvers.add(new ServletResponseMethodArgumentResolver());

  // Custom arguments
  if (getCustomArgumentResolvers() != null) {
    resolvers.addAll(getCustomArgumentResolvers());
  }

  // Catch-all
  resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

  return resolvers;
}

(3)ReturnValueHandler

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
	List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

	// Single-purpose return value types
	handlers.add(new ModelAndViewMethodReturnValueHandler());
	handlers.add(new ModelMethodProcessor());
	handlers.add(new ViewMethodReturnValueHandler());
	handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
												this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
	handlers.add(new StreamingResponseBodyReturnValueHandler());
	handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
												this.contentNegotiationManager, this.requestResponseBodyAdvice));
	handlers.add(new HttpHeadersReturnValueHandler());
	handlers.add(new CallableMethodReturnValueHandler());
	handlers.add(new DeferredResultMethodReturnValueHandler());
	handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

	// Annotation-based return value types
	handlers.add(new ModelAttributeMethodProcessor(false));
	handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
												this.contentNegotiationManager, this.requestResponseBodyAdvice));

	// Multi-purpose return value types
	handlers.add(new ViewNameMethodReturnValueHandler());
	handlers.add(new MapMethodProcessor());

	// Custom return value types
	if (getCustomReturnValueHandlers() != null) {
		handlers.addAll(getCustomReturnValueHandlers());
	}

	// Catch-all
	if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
		handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
	} else {
		handlers.add(new ModelAttributeMethodProcessor(true));
	}

	return handlers;
}

4.3 refresh -: 事件推送

/* Last step: publish corresponding event. */
protected void finishRefresh() {
  // Clear context-level resource caches (such as ASM metadata from scanning).
  // 清除resourceCaches资源缓存中的数据
  clearResourceCaches();

  // Initialize lifecycle processor for this context.
  // 为此上下文初始化生命周期处理器
  initLifecycleProcessor();

  // Propagate refresh to lifecycle processor first.
  // 首先将刷新完毕事件传播到生命周期处理器(触发isAutoStartup方法返回true的SmartLifecycle的start方法)
  getLifecycleProcessor().onRefresh();

  // Publish the final event.
  // => 推送上下文刷新完毕事件到相应的监听器  ===> DispatcherServlet#onRefresh() - 见#3.3
  publishEvent(new ContextRefreshedEvent(this));

  // Participate in LiveBeansView MBean, if active.
  LiveBeansView.registerApplicationContext(this);
}

SpringMVC启动过程:

Web 容器启动时会去读取 web.xml 这样的部署描述文件,相关组件启动顺序为:解析 <context-param> => 解析 <listener> => 解析 <filter> => 解析 <servlet> ,具体初始化过程如下:

1、解析 <context-param> 里的键值对。
2、创建一个 application 内置对象即 ServletContext,servlet 上下文,用于全局共享。
3、将 <context-param> 的键值对放入 ServletContext 即 application 中,Web 应用内全局共享。
4、读取 <listener> 标签创建监听器,一般会使用 ContextLoaderListener 类,Spring 就会创建一个 WebApplicationContext 类的对象,WebApplicationContext 类就是 IoC 容器,ContextLoaderListener 类创建的 IoC 容器是根 IoC 容器为全局性的,并将其放置在 appication 中,作为应用内全局`共享,键名为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,可以通过以下两种方法获取:

// [1]
WebApplicationContext applicationContext = 
  (WebApplicationContext) application.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
// [2]
WebApplicationContext applicationContext1 = WebApplicationContextUtils.getWebApplicationContext(application); 

这个全局的根 IoC 容器只能获取到在该容器中创建的 Bean 不能访问到其他容器创建的 Bean,也就是读取 web.xml 配置的 contextConfigLocation 参数的 xml 文件来创建对应的 Bean。

5、listener 创建完成后如果有 <filter> 则会去创建 filter。
6、初始化创建 <servlet> ,一般使用 DispatchServlet 类。
7、DispatchServlet 的父类 FrameworkServlet 会重写其父类的 initServletBean 方法,并调用 initWebApplicationContext() 以及 onRefresh() 方法。
8、initWebApplicationContext() 方法会创建一个当前 Servlet 的一个 IoC 子容器,如果存在上述的全局 WebApplicationContext 则将其设置为父容器,如果不存在上述全局的则父容器为 null。
9、读取 <servlet> 标签的 <init-param> 配置的 xml 文件并加载相关 Bean。
10、onRefresh() 方法创建 Web 应用相关组件。

5. DispatcherServlet 请求分析

视图版:

接口版:

5.1 入口分析

通过前面的分析,我们知道 DispatcherServlet 其本质还是 Servlet,那么当客户端的请求到达时,根据 Servlet 生命周期,其应该会调用其或者其父类中的 service 方法。在其父类 FrameworkServlet 中我们找到了 service 方法。

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    /**
     *  获取HttpMethod类型,
     *  HttpMethod为枚举类,支持的Http请求类型有GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
     */
    HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
        processRequest(request, response);
    }
    else {
        super.service(request, response);
    }
}

但是在这里似乎没有看到我们最想要的东西,那么我们来看一下其 doGet 和 doPost 方法。

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    processRequest(request, response);
}

protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    processRequest(request, response);
}

从这里我们可以分析到,doGet、doPost 等 Http 请求委托给了 processRequest() 方法进行处理。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    // 记录开始时间
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;

    // 提取LocaleContext和RequestAttributes属性,以便在请求结束后能从当前线程中恢复
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);

    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    // 初始化ContextHolder,将当前线程的LocaleContext和RequestAttributes绑定到ContextHolder
    initContextHolders(request, localeContext, requestAttributes);

    
    try {
        // => 调用doService方法做下一步处理
        doService(request, response);
    }
    catch (ServletException | IOException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (Throwable ex) {
        failureCause = ex;
        throw new NestedServletException("Request processing failed", ex);
    }
    finally {
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }
        logResult(request, response, failureCause, asyncManager);
        // 发布事件通知
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

该方法只是做了一些变量提取绑定、恢复、事件发布等工作,具体工作委托给了 doService 方法。

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

    logRequest(request);

    /**
     * 如果当前请求是一个 include request(不好翻译),如:<jsp:incluede page="xxx.jsp"/>
     * 则为此请求属性建立快照,以便include request结束后能够将其恢复
     */
    // Keep a snapshot of the request attributes in case of an include,
    // to be able to restore the original attributes after the include.
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        attributesSnapshot = new HashMap<>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    // Make framework objects available to handlers and view objects.
    // 将下列对象保存到request中,以便使用
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
    if (this.flashMapManager != null) {
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    }

    try {
        // => 真正开始处理HTTP请求
        doDispatch(request, response);
    }
    finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Restore the original attribute snapshot, in case of an include.
            // 恢复之前保存的数据快照
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }
}

该方法中依然没有看到对核心流程的处理,请求处理进一步委托给了 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 {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 1.尝试将当前请求转换为MultipartHttpServletRequest
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            // => 2.查找当前请求对应的handler,包括Handler(控制器)本身和Handler拦截器
            mappedHandler = getHandler(processedRequest);
            // 未能找到对应的handler,抛出NoHandlerFoundException异常并返回404
            if (mappedHandler == null) {
                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.
            // 4.处理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;
                }
            }

            // 5.应用前置拦截器
            // 如果有拦截器返回false,则表明该拦截器已经处理了返回结果,直接返回;
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler.
            // 6.调用HandlerAdapter的handler方法,真正开始处理Controller
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // 7.如果当前请求是并发处理,直接返回
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            // 8.为返回值设定默认视图名,如果当前返回值中不包含视图名的话
            applyDefaultViewName(processedRequest, mv);

            // 9.应用已注册拦截器的后置方法。
            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);
        }
        // 10.处理分发调用结果,如视图模型解析、返回等工作
        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);
            }
        }
    }
}

历经 service–>doGet–>processRequest–>doService–>doDispatch 终于到了核心方法。doDispatch 方法看似简单,但是其背后有复杂的业务逻辑支撑。

5.2 获取 Handler&HandlerAdapter

// TODO

5.3 HandlerAdapter#handle

// TODO

5.4 视图解析渲染

// TODO

posted @ 2024-05-15 07:02  tree6x7  阅读(5)  评论(0编辑  收藏  举报