SpringMVC源码解析(一)——初始化

前言

    本系列文章顺延“Spring源码解析”,是在“父容器”建立完成后,对“子容器”(SpringMVC)建立,以及请求处理的解析。java

 

源码解读

    提及 SpringMVC,DispatcherServlet 应该是最熟悉的类之一。它几乎掌控着整个请求的分发以及最终响应,咱们就从它来追溯“子容器”建立的源头。web

public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.Serializable {
    // getInitParameter可获取配置的 <init-param>
    // getServletName可获取配置的 <servlet-name>
    private transient ServletConfig config;

    // SpringMVC启动入口
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        // 子类实现
        this.init();
    }
}

    该类就是 DispatcherServlet 的最顶层抽象父类了,它实现了 Servlet.init 方法,经过 Servlet 规范咱们能够了解到,根据 <load-on-startup> 配置的不一样,调用时机也不一样。mvc

  • ≥0 :会在 Servlet 被实例化后执行;
  • <0:在该 Servlet 被第一次请求时才调用,不填默认走该逻辑
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {

    // GenericServlet.init调用
    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // 将 <init-param>键值对封装成 PropertyValue
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                // 将自身(DispatcherServlet)封装成 BeanWrapper,方便 Spring注入
                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;
            }
        }

        // 子类扩展实现
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }
}

    这一层主要是针对配置属性相关的代码,若是看过以前“实例建立”章节,对 bw.setPropertyValues(pvs, true) 应该不陌生,就是为 Servlet 作属性填充。接下来 initServletBean 方法实现了几乎“子容器”建立的大部分工做,由 FrameworkServlet 实现:app

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

    // 可用 <init-param>:contextClass指定
    private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
    // 默认 XmlWebApplicationContext
    public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;

    @Override
    protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
            // 初始化容器
            this.webApplicationContext = initWebApplicationContext();
            // 空实现:可自定义扩展
            initFrameworkServlet();
        }....// 省略 catch

		....// 省略日志
    }

    protected WebApplicationContext initWebApplicationContext() {
        // 获取父容器:经过调用 ServletContext.getAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)
        // 设置逻辑见容器启动: ContextLoader.initWebApplicationContext
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

        // 是经过构造器传入的方式会走这个分支(见 SpringBoot)
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                // 调用 refresh会置 active为 true
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        // 设置父容器
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            // 查找 contextAttribute是否已绑定的上下文 key
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // 若是尚未找到上下文,建立一个
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            // 子容器(SpringMVC),在容器刷新完毕后执行
            onRefresh(wac);
        }

        if (this.publishContext) {
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

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

    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {

        // 默认 XmlWebApplicationContext
        // 可用<init-param>:contextClass指定
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        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");
        }

        // 使用反射调用构造器实例化
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

        wac.setEnvironment(getEnvironment());
        // 设置父容器
        wac.setParent(parent);
        // 获取 <servlet>中配置的 <init-param>:contextConfigLocation
        String configLocation = getContextConfigLocation();
        if (configLocation != null) {
            // 设置“mvc”配置文件所在路径
            wac.setConfigLocation(configLocation);
        }
        // 和父容器类似的套路
        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // 设置 ID
            if (this.contextId != null) {
                wac.setId(this.contextId);
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
            }
        }

        // Servelt相关的设置
        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.setNamespace(getNamespace());

        // 注册事件监听器:被 SourceFilteringListener包裹,仅在“子容器”(mvc)中触发
        // ContextRefreshListener:触发 FrameworkServlet.onApplicationEvent
        // onApplicationEvent进而调用:onRefresh
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

        // 任何状况下 initPropertySources都在上下文刷新时被调用
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
        }

        // 空实现
        postProcessWebApplicationContext(wac);

        // ApplicationContextInitializer执行,由如下属性指定
        // <init-param>:globalInitializerClasses、contextInitializerClasses指定
        applyInitializers(wac);

        // 容器刷新:AbstractApplicationContext实现(同父容器)
        wac.refresh();
    }
}

    抛开一些设置属性的步骤不谈,上面的逻辑主要能够总结为:容器实例化(反射调用构造器)——>将<init-param>指定的“mvc”配置文件设置到容器,以便随后的解析——>而后注册了一个“容器刷新”事件监听器,以便回调——>调用 AbstractApplicationContext.refresh 刷新容器——>最后回调 onRefresh框架

    这里的 onRefresh 在 FrameworkServlet 中是空实现,由子类定制实现。来看看 DispatcherServlet 的实现吧。ide

public class DispatcherServlet extends FrameworkServlet {

    // 在 refresh中的 finishRefresh中会发布“刷新”事件
    // 会触发 ContextRefreshListener监听器,继而调用该方法
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    // 主要初始化了 mvc中所必须的成员变量 
    protected void initStrategies(ApplicationContext context) {
        // 若是定义了 MultipartResolver,实例化
        initMultipartResolver(context);

        // 若是定义了 LocaleResolver,实例化
        // 默认使用 AcceptHeaderLocaleResolver
        initLocaleResolver(context);

        // 若是定义了 ThemeResolver,实例化
        // 默认使用 FixedThemeResolver
        initThemeResolver(context);

        // 初始化全部的 HandlerMapping:具体处理器
        // 初始化参数 detectAllHandlerMappings设为 false能够只加载指定的bean
        initHandlerMappings(context);

        // 初始化全部的 HandlerAdapter
        // 初始化参数 detectAllHandlerAdapters设为 false能够只加载指定的bean
        initHandlerAdapters(context);

        // 初始化全部的 HandlerExceptionResolver(异常处理)
        initHandlerExceptionResolvers(context);

        // 若是定义了 RequestToViewNameTranslator,实例化
        // 默认使用 DefaultRequestToViewNameTranslator
        initRequestToViewNameTranslator(context);

        // 初始化全部的 ViewResolver
        // 默认使用 InternalResourceViewResolver
        initViewResolvers(context);

        // 若是定义了 FlashMapManager,实例化
        // 默认使用 DefaultFlashMapManager
        initFlashMapManager(context);
    }
}

    以上的这些就是“子容器”和“父容器”差别之处了,支持了“mvc”框架例如文件上传请求映射异常处理视图解析等功能。咱们会在以后的章节展开讲解。post

 

总结

    本篇主要梳理了“子容器”(SpringMVC)的初始化脉络,顶层父类留下可扩展的“口子”,让子类去实现具体的初始化逻辑。ui

相关文章
相关标签/搜索