Spring MVC 初始化源码—DispatcherServlet与子容器的初始化以及MVC组件的初始化一万字

Posted L-Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC 初始化源码—DispatcherServlet与子容器的初始化以及MVC组件的初始化一万字相关的知识,希望对你有一定的参考价值。

  基于最新Spring 5.x,详细介绍了Spring MVC 初始化流程的源码,主要包括DispatcherServlet与MVC子容器的初始化,以及各种MVC组件的初始化。

  上一篇文章我们讲解了ContextLoaderListener监听器与根上下文容器的初始化。
  ContextLoaderListenercontextInitialized方法回调完毕之后,Root WebApplicationContext初始化完毕,随后会初始化全部的Filter,并且执行init回调,最后会按顺序初始化全部的即时创建的Servlet,对于Spring MVC来说,最重要的就是DispatcherServlet,该过程同时会涉及到MVC子容器的创建和初始化,以及各种MVC组件的初始化。一起来看看DispatcherServlet的初始化源码吧!

  下面的源码版本基于5.2.8.RELEASE

Spring MVC源码 系列文章

Spring MVC 初始化源码(1)—ContextLoaderListener与父上下文容器的初始化

Spring MVC 初始化源码(2)—DispatcherServlet与子容器的初始化以及MVC组件的初始化【一万字】

Spring MVC 初始化源码(3)—<mvc:annotation-driven >配置标签的源码解析

Spring MVC 初始化源码(4)—@RequestMapping注解的源码解析

Spring MVC 请求执行流程的源码深度解析【两万字】

1 DispatcherServlet的概述

  DispatcherServlet作为Spring MVC的核心类,基本上所有的请求都是通过该Servlet来进行分发。此前的Spring MVC的学习系列文章中我们已经详细学习了它的功能和流程,现在我们来学习它的源码。
  DispatcherServlet的uml类图如下:
在这里插入图片描述
  可以看到DispatcherServlet最终继承了HttpServlet方法,实现了Servlet接口,因此它也是一个JavaEE中的Servlet标准实现,并且主要处理HTTP请求。
  HttpServletBean是HttpServlet的简单的抽象实现,主要功能是解析web.xml文件中的<servlet/>标签下面的<init-param/>标签配置的参数,将其设置为bean的对应的属性。该Servlet将具体的请求处理留给子类实现,仅仅继承HttpServlet的默认行为(doGet,doPost等)。
  FrameworkServlet是Spring Web框架的基础servlet。该类的功能有两个:

  1. 为每个该类型的servlet关联一个子WebApplicationContext实例。Servlet的配置由Servlet名称空间中的bean确定。
  2. 发布有关请求处理的事件,无论是否成功处理了请求。子类必须实现doService方法以执行真正的请求处理。

  DispatcherServlet是HTTP请求处理程序/控制器的中央调度程序,在初始化的时候它会初始化各个功能组件的实现类,并且在后续实现具体的请求处理流程,主要是通过调度各个组件来处理请求,几乎可以处理所有请求。
  DispatcherServlet的一系列初始化操作基本都是在init方法中完成的,这个init方法就是Servelet接口提供初始化方法,因此我们从该方法入手。

/**
 * GenericServlet的属性
 * 保存了ServletConfig参数
 */
private transient ServletConfig config;

/**
 * GenericServlet的init方法
 *
 * @param config Servlet配置
 */
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    //调用初始化方法
    this.init();
}

/**
 1. GenericServlet的init方法
 2. <p>
 3. 应该被子类重写的方法
 */
public void init() throws ServletException {}

  该方法整体来说做了三件事:

  1. 将当前ServletConfiginit parameters参数填充到当前DispatcherServlet的实例的对应的属性中。可以在web.xml中对应的<Servlet>标签下通过设置<init-param>标签来配置各种属性。主要是HttpServletBean#init()方法。
  2. 初始化于此Servlet关联的MVC子容器,该容器的父容器就是Root Application(可能为null),随后会以属性名:org.springframework.web.servlet.FrameworkServlet.CONTEXT. + servletNamevalue为此容器的方法存入ServletContext的属性中。主要是FrameworkServlet#initServletBean方法。
  3. 初始化Servlet使用的MVC组件。主要是DispatcherServlet#onRefresh方法。

2 HttpServletBean#init()设置init属性

  HttpServletBeaninit()方法就是将当前ServletConfiginit parameters参数填充到当前DispatcherServlet的实例的对应的属性中。
  也就是将web.xml<servlet/>标签下的<init-param/>标签定义的属性设置到对应Servlet实例的对应名字的属性中,最常见属性就是contextConfigLocation、namespace等等。
  在属性填充完毕之后们将会调用initServletBean方法,HttpServletBean提供了空的实现,主要用于被子类重写并实现自定义的扩展逻辑。它的子类FrameworkServlet就重写了该方法。

/**
 * GenericServlet的init方法
 * <p>
 * 应该被子类重写的方法
 */
public void init() throws ServletException {
}

/**
 * HttpServletBean的属性
 * <p>
 * 必须的参数集合,默认是一个空集合
 */
private final Set<String> requiredProperties = new HashSet<>(4);

/**
 * HttpServletBean的方法
 * <p>
 * 将配置参数映射到该servlet的bean属性上,并调用子类初始化。
 *
 * @throws ServletException 如果bean属性无效(或缺少必需的属性),或者子类初始化失败。
 */
@Override
public final void init() throws ServletException {
    /*
     * 1 新建一个ServletConfigPropertyValues实例,根据Servlet的init参数设置bean属性
     * 就是将ServletConfig内部的init parameters初始化参数存储到ServletConfigPropertyValues中
     * 如果有requiredProperties中的必须属性没有设置,那么抛出ServletException
     */
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    //如果有参数
    if (!pvs.isEmpty()) {
        try {
            //获取当前DispatcherServlet 对象的BeanWrapper对象,以JavaBeans的样式便捷的访问属性。
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            //ServletContext的资源加载器
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            //自定义的属性编辑器
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            //初始化DispatcherServlet的BeanWrapper,该方法是一个空实现
            initBeanWrapper(bw);
            /*
             * 2 通过BeanWrapper便捷的将解析出来的属性集合设置给DispatcherServlet的各种属性
             */
            bw.setPropertyValues(pvs, true);
        } catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }
    /*
     * 3 扩展接口,让子类进行他们自定义的初始化
     * 子类FrameworkServlet就重写了该方法
     */
    initServletBean();
}

/**
 * HttpServletBean的方法
 * <p>
 * 子类可以重写此方法以执行自定义初始化。
 * 在调用此方法之前,将设置此servlet的所有bean属性。
 * <p>
 * 此默认实现为空。
 */
protected void initServletBean() throws ServletException {
}

3 FrameworkServlet#initServletBean初始化MVC容器

  FrameworkServlet重写的initServletBean方法。
  其内部首先调用initWebApplicationContext方法用于初始化并发布此Servlet关联的WebApplicationContext容器,随后调用initFrameworkServlet方法初始化FrameworkServlet本身,子类可以重写此方法以执行其所需的任何初始化,默认实现为空。

/**
 * FrameworkServlet的属性
 * <p>
 * 此Servlet关联的WebApplicationContext
 */
@Nullable
private WebApplicationContext webApplicationContext;

/**
 1. FrameworkServlet的方法
 2. <p>
 3. 设置所有bean属性后调用的方法,主要目的是创建此Servlet的WebApplicationContext。
 */
@Override
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 {
        /*
         * 1 初始化并发布此Servlet关联的WebApplicationContext,核心方法
         */
        this.webApplicationContext = initWebApplicationContext();
        /*
         * 2 初始化FrameworkServlet,在设置任何bean属性并加载WebApplicationContext后,将调用此方法。
         *
         * 子类可以重写此方法以执行其所需的任何初始化,默认实现为空。
         */
        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");
    }
}

3.1 initWebApplicationContext初始化子MVC容器

  该方法用于初始化并发布此Servlet关联的WebApplicationContext,也就是子容器。实际上大部分情况是通过内部的createWebApplicationContext方法实际创建上下文,可以在子类中覆盖。

  1. 获取Root WebApplicationContext作为父上下文,Root WebApplicationContext是通过ContextLoaderListener初始化的,可以为null。
  2. 如果webApplicationContext属性不为null,那么直接调用configureAndRefreshWebApplicationContext配置并刷新该WebApplicationContext,一般都是null。
  3. 如果此前没有关联的WebApplicationContext,那么首先会尝试查找现成的context。首先当前Servlet中获取名为contextAttribute<init-param/>初始化参数的值,该值作为属性名用于检索该servlet应该使用的WebApplicationContext。然后从当前ServletContext中调用getAttribute方法基于该属性名获取对应属性值。如果获取的属性值不为null,那么将该值作为已初始化完成了的WebApplicationContext实例返回,如果获取的属性值为null,那么抛出:“err.servlet_config_not_initialized“异常。一般都不会找到。
  4. 如果在属性中没有指定的WebApplicationContext实例,一般来说都没有。那么调用createWebApplicationContext实例化此servlet关联的WebApplicationContext,可以是默认的XmlWebApplicationContext或自定义上下文类(如果已设置)
  5. 如果此前还没有调用onRefresh方法,那么调用一次onRefresh方法,该方法是一个可以被子类重写的模板方法,用于特定的servlet的添加自定义的刷新工作,在成功刷新WebApplicationContext后调用。
    1. 如果是新建子MVC容器,容器刷新完毕后会发送ContextRefreshedEvent事件,会触发ContextRefreshListener监听器回调。该监听器的回调就是执行FrameworkServlet的onApplicationEvent方法,内部就会执行该方法,并更改标志为true。
    2. 如果上下文是不具有刷新支持的ConfigurableApplicationContext,或者在构造时注入的上下文已被刷新,那么标志就是false。
  6. 如果应该将此WebApplicationContext发布为ServletContext的属性(默认需要设置),那么就设置为ServletContext的一个属性。属性名为org.springframework.web.servlet.FrameworkServlet.CONTEXT.+servletName
/**
 * FrameworkServlet的方法
 * <p>
 * 初始化并发布此Servlet关联的WebApplicationContext。
 * 通过内部的createWebApplicationContext方法实际创建上下文,可以在子类中覆盖。
 *
 * @return WebApplicationContext实例
 */
protected WebApplicationContext initWebApplicationContext() {
    /*
     * 1 获取Root WebApplicationContext
     * 父上下文通过ContextLoaderListener初始化,可以为null
     */
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    /*
     * 2 如果webApplicationContext属性不为null,那么直接配置并刷新该WebApplicationContext
     */
    if (this.webApplicationContext != null) {
        //到这里表示在构造时注入了一个上下文实例->直接使用它
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            // 上下文尚未刷新->提供诸如设置父上下文,设置应用程序上下文ID等服务。
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    //上下文实例是在没有显式父级的情况下注入的->将根应用程序上下文(如果有可能为null)设置为父级
                    cwac.setParent(rootContext);
                }
                //配置并初始化此上下文容器
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    /*
     * 3 如果wac为null,即此前没有关联的WebApplicationContext
     *
     * 首先当前Servlet中获取名为contextAttribute的<init-param/>初始化参数的值,
     * 该值作为属性名用于检索该servlet应该使用的WebApplicationContext。
     * 然后从当前ServletContext中调用getAttribute方法基于该属性名获取对应属性值。
     * 如果获取的属性值不为null,那么将该值作为已初始化完成了的WebApplicationContext实例返回,
     * 如果获取的属性值为null,那么抛出:“err.servlet_config_not_initialized“异常。
     */
    if (wac == null) {
        wac = findWebApplicationContext();
    }
    /*
     * 4 如果wac还是为null,即在属性中没有指定的WebApplicationContext实例,一般来说都没有
     *
     * 那么实例化此servlet关联的WebApplicationContext,可以是默认的XmlWebApplicationContext或自定义上下文类(如果已设置)。
     */
    if (wac == null) {
        // No context instance is defined for this servlet -> create a local one
        wac = createWebApplicationContext(rootContext);
    }
    /*
     * 5 如果此前还没有调用onRefresh方法
     *
     * 那么调用一次onRefresh方法,该方法是一个可以被子类重写的模板方法
     * 用于特定的servlet的添加自定义的刷新工作,在成功刷新WebApplicationContext后调用。
     *
     * 如果是新建子MVC容器,容器刷新完毕后会发送ContextRefreshedEvent事件,会触发ContextRefreshListener监听器回调
     * 该监听器的回调就是执行FrameworkServlet的onApplicationEvent方法,内部就会执行该方法,并更改标志为true
     * 如果上下文是不具有刷新支持的ConfigurableApplicationContext,或者在构造时注入的上下文已被刷新,那么标志就是false
     */
    if (!this.refreshEventReceived) {
        //上下文是不具有刷新支持的ConfigurableApplicationContext,或者在构造时注入的上下文已被刷新
        //那么在此处手动触发onRefresh初始化方法
        synchronized (this.onRefreshMonitor) {
            onRefresh(wac);
        }
    }
    /*
     * 6 如果应该将此WebApplicationContext发布为ServletContext的属性,那么就设置为属性,默认需要设置
     */
    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        //属性名为org.springframework.web.servlet.FrameworkServlet.CONTEXT. + servletName
        String attrName = getServletContextAttributeName();
        //设置为ServletContext的一个属性
        getServletContext().setAttribute(attrName, wac);
    }
    return wac;
}


/**
 * FrameworkServlet的方法
 * <p>
 * WebApplicationContext的ServletContext属性的前缀
 */
public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";

/**
 1. 返回此Servlet关联的WebApplicationContext在ServletContext中的属性名称。
 2. 默认实现返回SERVLET_CONTEXT_PREFIX + servletName
 */
public String getServletContextAttributeName() {
    return SERVLET_CONTEXT_PREFIX + getServletName();
}

3.1.1 configureAndRefreshWebApplicationContext配置并刷新子容器

  该方法用于配置并刷新此Servlet关联的MVC子容器,步骤类似于此前文章中讲过的Root容器的同名方法。
  大概步骤如下:

  1. 设置该应用程序上下文的id(一般用不到),默认id就是"org.springframework.web.context.WebApplicationContext:"+项目路径+"/"+servletName,可以通过在web.xml中的<Servlet/>标签下配置名为contextId<init-param/>初始化参数来自定义子容器id。
  2. 将此项目的ServletContext设置给上下文的servletContext属性,将此Servlet的ServletConfig设置给上下文的servletConfig属性,将此Servlet的namespace设置给上下文的namespace属性,手动添加一个监听器SourceFilteringListener
    1. SourceFilteringListener内部包装了一个ContextRefreshListener,容器刷新完毕后会发送ContextRefreshedEvent事件,此时会触发监听器的回调,该监听器的回调就是执行FrameworkServlet的onApplicationEvent方法
  3. 获取容器的Environment环境变量对象,随后调用initPropertySources方法手动初始化Servlet属性源,该方法在refresh()刷新容器的方法之前执行,以确保servlet属性源已准备就绪,可以被refresh()方法正常使用。
  4. 调用postProcessWebApplicationContext方法,在刷新给定的WebApplicationContext并将其激活关联为该Servlet的上下文之前,对其进行后处理,即自定义容器。目前是一个空实现,子类可以重写。
  5. 调用applyInitializers方法用于继续对容器执行自定义操作。默认实现是通过web.xml中配置的<context-param>全局参数globalInitializerClasses,和来自当前<Servlet/>内部的<init-param>初始化参数contextInitializerClasses来确定指定了哪些ApplicationContextInitializer类,并执行初始化,随后使用AnnotationAwareOrderComparator排序(支持PriorityOrdered接口、Ordered接口、@Ordered注解、@Priority注解的排序),最后按照排序优先级从高到低依次调用每一个实例的initialize方法来初始化给定的servletContext。
    6 调用容器的refresh方法执行刷新操作,这是核心方法,我们在此前IoC容器初始化源码部分已经着重讲解了。该方法将会初始化容器,包括解析配置文件,创建Spring bean实例,执行各种回调方法等等……操作(源码非常多)。
/**
 * FrameworkServlet的属性
 * <p>
 * 要分配的WebApplicationContext ID,可通过<init-param/>参数配置
 */
@Nullable
private String contextId;

/**
 * FrameworkServlet的方法
 * <p>
 * 配置并刷新新建的mvc WebApplicationContext
 * 该方法中会配置一系列Servlet的属性,初始化并调用ApplicationContextInitializer的扩展点(用于自定义root context),最后会执行refresh刷新容器。
 *
 * @param wac 此servlet关联的上下文
 */
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    /*
     * 1 如果wac的全路径identity字符串形式等于wac的id,那么设置应用程序上下文的id
     */
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        //应用程序上下文ID仍设置为其原始默认值->可以通过<init-param/>参数配置
        if (this.contextId != null) {
            wac.setId(this.contextId);
        } else {
            //产生预设id,默认规则就是"org.springframework.web.context.WebApplicationContext:"+项目路径+"/"+servletName
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
        }
    }
    /*
     * 2 配置一系列属性
     */
    //将此项目的ServletContext设置给上下文的servletContext属性
    wac.setServletContext(getServletContext());
    //将此Servlet的ServletConfig设置给上下文的servletConfig属性
    wac.setServletConfig(getServletConfig());
    //将此Servlet的namespace设置给上下文的namespace属性
    wac.setNamespace(getNamespace());
    //手动添加一个监听器SourceFilteringListener
    //在容器刷新完毕之后会发送ContextRefreshedEvent事件,此时就会触发监听器的回调
    //该监听器的回调就是执行FrameworkServlet的onApplicationEvent方法
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

    /*
     * 3 获取容器的Environment环境变量对象,随后调用initPropertySources方法手动初始化属性源
     * 该方法在refresh()刷新容器的方法之前执行,以确保servlet属性源已准备就绪,可以被正常使用
     */
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }
    /*
     * 4 在刷新给定的WebApplicationContext并将其激活关联为该Servlet的上下文之前,对其进行后处理
     * 目前是一个空实现,子类可以重写
     */
    postProcessWebApplicationContext(wac);
    /*
     * 5 对当前的WebApplicationContext实例应用给定的ApplicationContextInitializer,以实现自定义上下文的逻辑
     * 这类似于在初始化Root WebApplicationContext的时候调用的customizeContext方法
     */
    applyInitializers(wac);
    /*
     * 6 刷新(初始化)容器
     * 这是核心方法,我们在此前IoC容器初始化源码部分已经着重讲解了
     */
    wac.refresh();
}

3.1.1.1 getNamespace获取名称空间

  Servlet对应的容器的nameSpace就被设置为Servlet的namespace,可以通过设置该Servlet的名为nameSpace<init-param>初始化参数手动指定名称空间,如未指定,那么默认名称空间为servletName+"-servlet",即如果此servlet的servlet-name为"test",则该servlet使用的默认名称空间将解析为"test-servlet"

/**
 * FrameworkServlet中的常量属性
 * <p>
 * WebApplicationContext名称空间的后缀。
 * 如果此类的servlet在上下文中被命名为"test",则servlet使用的默认名称空间将解析为"test-servlet"。
 */
public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";

/**
 * FrameworkServlet的方法
 * <p>
 * 获取此nameSpace
 * <p>
 * 返回此servlet的名称空间,如果未设置自定义名称空间,则返回默认方案:
 * 即默认nameSpace为servletName+"-servlet",也可以通过设置Servlet的nameSpace属性手动指定名称空间
 */
public String getNamespace() {
    return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}

3.1.1.2 applyInitializers应用ApplicationContextInitializer扩展

  基于ApplicationContextInitializer自定义MVC子容器,这个方法类似于在初始化Root WebApplicationContext的时候调用的customizeContext方法。
  默认实现是通过web.xml中配置的<context-param>全局参数globalInitializerClasses,和来自当前<Servlet/>内部的<init-param>初始化参数contextInitializerClasses来确定指定了哪些ApplicationContextInitializer类,并执行初始化,随后使用AnnotationAwareOrderComparator排序(支持PriorityOrdered接口、Ordered接口、@Ordered注解、@Priority注解的排序),最后按照排序优先级从高到低依次调用每一个实例的initialize方法来初始化给定的servletContext。

//FrameworkServlet的属性

/**
 * 实际要应用于上下文的ApplicationContextInitializer实例。
 */
private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
        new ArrayList<>();

/**
 * 在设置的ApplicationContextInitializer的全路径类名
 */
@Nullable
private String contextInitializerClasses;

/**
 * FrameworkServlet的方法
 * <p>
 * 在对给定容器应用刷新之前调用所有的ApplicationContextInitializer,一起带实现自定义容器的逻辑
 * 这个方法类似于在初始化Root WebApplicationContext的时候调用的customizeContext方法
 *
 * @param wac 配置的WebApplicationContext(尚未刷新)
 */
protected void applyInitializers(ConfigurableApplicationContext wac) {
    /*
     * 1 获取并初始化全局ApplicationContextInitializer
     */
    //从servletContext中尝试获取globalInitializerClasses全局参数的值
    String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM);
    //如果设置了该全局参数
    if (globalClassNames != null) {
        //根据",; \\t\\n"拆分值字符串
        for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
            //根据给定的全路径名字符串初始化指定的ApplicationContextInitializer实例并加入contextInitializers集合中
            this.contextInitializers.add(loadInitializer(className, wac));
        }
    }
    /*
     * 1 获取并初始化当前Servlet的ApplicationContextInitializer
     */
    //当前Servlet如果设置了名为contextInitializerClasses的<init-param>初始化参数
    if (this.contextInitializerClasses != null) {
        //根据",; \\t\\n"拆分值字符串
        for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) {
            //根据给定的全路径名字符串初始化指定的ApplicationContextInitializer实例并加入contextInitializers集合中
            this.contextInitializers.add(loadInitializer(className, wac));
        }
    }
    /*
     * 3 对该集合进行Order排序,可以支持PriorityOrdered接口、Ordered接口、@Ordered注解、@Priority注解的排序
     * 比较优先级为PriorityOrdered>Ordered>@Ordered>@Priority

以上是关于Spring MVC 初始化源码—DispatcherServlet与子容器的初始化以及MVC组件的初始化一万字的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC 初始化源码—@RequestMapping注解的源码解析

Spring MVC源码——Root WebApplicationContext

Spring6源码・MVC请求处理流程源码解析

Spring MVC 初始化源码—DispatcherServlet与子容器的初始化以及MVC组件的初始化一万字

一文彻底解密Spring 源码之Spring MVC

一文彻底解密Spring 源码之Spring MVC