Spring中DispatcherServlet的初始化过程

在分析请求的处理流程之前,还是很有必要清楚DispatcherServlet的初始化过程是怎么回事,毕竟这个过程会初始化很多处理请求的组件,不然不知道这些组件是在什么之后以及怎么样被创建的。所以本文以Servlet规范的init方法作为入口,分析一下整个DispatcherServlet的初始化过程。


DispatcherServlet的继承体系

黄线上面的3个接口或类属于是Servlet规范提供的,下面3个类是Spring框架提供的。Servlet容器最开始调用上层的init方法,然后调用链条依次向下。

GenericServlet

在上面整个继承体系中,只有该类实现了Servlet接口的init方法,所以一开始会调用该方法。该类中的实现很简单,就只是赋值保存了一下ServletConfig对象,然后调用该类定义的无参的init方法,这是一个模板方法,由子类重写。在上面的继承体系中,HttpServletBean重写了该方法。

v8.5.59
java
java/javax/servlet/GenericServlet.java
@Override
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}
// 模板方法,由子类实现
public void init() throws ServletException {
    // NOOP by default
}

HttpServletBean

java
spring-webmvc/src/main/java/org/springframework/web/servlet/HttpServletBean.java
/*
 * Servlet容器初始化DispatcherServlet时,首先执行的是该方法
 */
@Override
public final void init() throws ServletException {

    // Set bean properties from init parameters.
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            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;
        }
    }

    // Let subclasses do whatever initialization they like.
    // 允许子类做额外的初始化操作,但不允许其重写init方法,所以为什么init方法被final修饰。
    initServletBean();
}
protected void initServletBean() throws ServletException {
    // 模板方法,让子类实现
}

FrameworkServlet

initServletBean
initWebApplicationContext
<
>
java
spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
@Override
protected final void initServletBean() throws ServletException {

    try {
        /*
         * 初始化Spring的子容器
         */
        this.webApplicationContext = initWebApplicationContext();
        /*
         * 一样的套路,留给子类实现初始化操作
         * 不过DispatcherServlet并没有重写该方法,所以这里是空操作。
         */
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        throw ex;
    }

}
protected void initFrameworkServlet() throws ServletException {
}
java
spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
protected WebApplicationContext initWebApplicationContext() {
    // 从servlet上下文中获取spring的root容器
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    /*
     * 在Servlet容器启动时,如果经过了AbstractDispatcherServletInitializer的处理,那么这里肯定是不会为null的
     * this.webApplicationContext是在AbstractDispatcherServletInitializer处创建DispatcherServlet时进行赋值的
     */
    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;
            // 如果未被激活,在SpringBoot中这里不满足条件
            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 -> set
                    // the root application context (if any; may be null) as the parent
                    // 将root容器设置为父容器
                    cwac.setParent(rootContext);
                }
                // 配置和刷新web应用上下文
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    // 在SpringBoot的默认自动配置下,这里不满足条件
    if (wac == null) { // 如果是常见的基于web.xml的方式,这里满足条件
        // No context instance was injected at construction time -> see if one
        // has been registered in the servlet context. If one exists, it is assumed
        // that the parent context (if any) has already been set and that the
        // user has performed any initialization such as setting the context id
        wac = findWebApplicationContext();
    }
    // 在SpringBoot的默认自动配置下,这里不满足条件
    if (wac == null) { // 一般这里仍然为null,符合条件
        // No context instance is defined for this servlet -> create a local one
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) { // 确保只会执行一次
        // Either the context is not a ConfigurableApplicationContext with refresh
        // support or the context injected at construction time had already been
        // refreshed -> trigger initial onRefresh manually here.
        synchronized (this.onRefreshMonitor) {
            // 调用子类的重写实现
            onRefresh(wac);
        }
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        // 获取web上下文的名称
        String attrName = getServletContextAttributeName();
        // 把web上下文也放入到servlet的上下文中
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}
protected void onRefresh(ApplicationContext context) {
    // For subclasses: do nothing by default.
}

这里会涉及到Spring中子容器的初始化,然后调用模板方法initFrameworkServlet,但是默认是空实现,而且DispatcherServlet没有重写该方法,所以这里没有做任何操作。 在initWebApplicationContext中会调用onRefresh这个模板方法,下面来看看DispatcherServlet中是怎么实现的?

DispatcherServlet

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
    // 初始化文件上传解析器
    initMultipartResolver(context);
    // 初始化本地化解析器
    initLocaleResolver(context);
    // 初始化主题解析器
    initThemeResolver(context);
    // 初始化handler映射
    initHandlerMappings(context);
    // 初始化handler适配器
    initHandlerAdapters(context);
    // 初始化handler异常解析器
    initHandlerExceptionResolvers(context);
    // 初始化请求视图名转换器
    initRequestToViewNameTranslator(context);
    // 初始化视图解析器
    initViewResolvers(context);
    // 初始化flash映射管理器
    initFlashMapManager(context);
}

可以看到,这里初始化了很多组件,本文选择一部分重要的组件来介绍。在介绍具体的组件加载之前,介绍一下 DispatcherServlet的默认策略机制。

默认策略

DispatcherServlet在初始化各种资源时,如果发现bean工厂中没有相关的bean,则可以使用预定义好的默认配置。

getDefaultStrategies
DispatcherServlet.properties
<
>
java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    if (defaultStrategies == null) {
        try {

            // 加载类路径下属性文件
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            // 从dispatcherServlet.properties文件中加载默认的策略
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
    }

    // 获取策略类型,即要获取默认配置哪种组件
    String key = strategyInterface.getName();
    // 获取预配置好的组件名称
    String value = defaultStrategies.getProperty(key);
    if (value != null) {
        // 字符串分割
        String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
        // 保存实例化好的组件对象
        List<T> strategies = new ArrayList<>(classNames.length);
        // 遍历类名
        for (String className : classNames) {
            try {
                // 加载类
                Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                // 对类进行实例化
                Object strategy = createDefaultStrategy(context, clazz);
                // 添加到列表中
                strategies.add((T) strategy);
            }
        }
        // 返回组件对象列表
        return strategies;
    }
    else {
        return Collections.emptyList();
    }
}
properties
spring-webmvc/src/main/resources/org/springframework/web/servlet/DispatcherServlet.properties

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

initMultipartResolver

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
/** Well-known name for the MultipartResolver object in the bean factory for this namespace. */
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
private void initMultipartResolver(ApplicationContext context) {
    try {
        /*
         * 获取名为multipartResolver的bean
         */
        this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
    }
}

这里在从bean工厂中查找名称为multipartResolver的bean。在Spring Boot中,默认提供了一个叫作MultipartAutoConfiguration的自动配置类,会注册一个名称就是multipartResolver的bean。

v2.7.x
java
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfiguration.java
/*
 * 默认是StandardServletMultipartResolver,而不是CommonsMultipartResolver
 */
@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
@ConditionalOnMissingBean(MultipartResolver.class)
public StandardServletMultipartResolver multipartResolver() {
    // 创建StandardServletMultipartResolver对象
    StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
    multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
    return multipartResolver;
}

initHandlerMappings

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    // 默认为true
    if (this.detectAllHandlerMappings) {
        // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        // 从bean工厂中获取所有的HandlerMapping类型的bean
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        // 如果获取到了bean
        if (!matchingBeans.isEmpty()) {
            // 转换成链表
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // We keep HandlerMappings in sorted order.
            // 进行排序
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }

    /*
     * 在Spring Boot的默认自动配置下,上面是会获取到bean的,所以这里是不满足条件的。
     */
    if (this.handlerMappings == null) {
        // 获取默认策略
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    }

    for (HandlerMapping mapping : this.handlerMappings) {
        // 是否使用路径模式
        if (mapping.usesPathPatterns()) {
            this.parseRequestPath = true;
            break;
        }
    }
}

其实这里主要关注RequestMappingHandlerMapping就可以了,这是最常用的一种,而且排序后在列表中的第一个。

initHandlerAdapters

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;

    // 默认是true
    if (this.detectAllHandlerAdapters) {
        // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
        // 从bean工厂中加载所有HandlerAdapter类型的bean
        Map<String, HandlerAdapter> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        // 如果加载到bean
        if (!matchingBeans.isEmpty()) {
            // 将加载到的bean放入列表中
            this.handlerAdapters = new ArrayList<>(matchingBeans.values());
            // We keep HandlerAdapters in sorted order.
            // 进行排序
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    }

    /*
     * 如果上面没有加载到bean,则使用默认策略。
     * 但是在Spring Boot的默认自动配置下,这里是不会满足条件的。
     */
    if (this.handlerAdapters == null) {
        // 获取默认策略
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
    }
}

这里关注最常用的RequestMappingHandlerAdapter就行,排序后位于列表第一个。

initHandlerExceptionResolvers

java
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java
private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;

    if (this.detectAllHandlerExceptionResolvers) { // 默认为true
        // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
        // 从bean工厂中获取所有类型为HandlerExceptionResolver的bean
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        // 如果获取到bean
        if (!matchingBeans.isEmpty()) {
            // 将bean放入列表中
            this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
            // We keep HandlerExceptionResolvers in sorted order.
            // 进行排序
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }

    // 上面没有加载到bean
    if (this.handlerExceptionResolvers == null) {
        // 获取默认策略
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
    }
}

其实这几个方法的逻辑都差不多,Spring完全可以将其公共逻辑抽取出来,不同点可以通过参数来支持。

总结

本文介绍了Spring中DispatcherServlet的初始化过程,比较重要的是初始化了一些组件。这些组件就是用来处理网络请求的,后续会深入各个组件的工作原理。