SpringMVC源码阅读-Root WebApplicationContext初始化(一)

说明

Root WebApplicationContext 因为容器有父子关系 只是表示是最父级WebApplicationContext   WebApplicationContext是一个接口 默认使用的是XmlWebApplicationContext

1.传统spring mvc配置

<!-- 省略非关键的配置 -->

<!-- [1] Spring配置 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 指定Spring Bean的配置文件所在目录。默认配置在WEB-INF目录下 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:config/applicationContext.xml</param-value>
</context-param>

<!-- ====================================== -->

<!-- [2] Spring MVC配置 -->
<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-servlet.xml</param-value> // 默认
    </init-param>
    -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

现在spring boot已经不需要配置了。通过自动配置代替了手动配置。但是这些配置还是存在

ContextLoaderListener

作用是在启动tomcat 或者jetty服务器的时候初始化一个Root Spring WebApplicationContext 

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    //负责初始化容器
    public void contextInitialized(ServletContextEvent event) {
//<1>
this.initWebApplicationContext(event.getServletContext()); } //负责销毁容器 public void contextDestroyed(ServletContextEvent event) { this.closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }

<1>initWebApplicationContext

org.springframework.web.context.ContextLoader#initWebApplicationContext

/**
     * WebApplicationContext.class.getName() + ".ROOT"
     * @param servletContext
     * @return
     */
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        /**
         * 
         * 因为初始化后会set到setvletConontext
         * WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName() + ".ROOT"
         * 防止重复初始化root WebApplicationContext
         */
        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!");
        } else {
            Log logger = LogFactory.getLog(ContextLoader.class);
            servletContext.log("Initializing Spring root WebApplicationContext");
            if (logger.isInfoEnabled()) {
                logger.info("Root WebApplicationContext: initialization started");
            }

            long startTime = System.currentTimeMillis();

            try {
                if (this.context == null) {
                    /**
                     * <2>
                     *获得对应的context
                     * 1.servletContext.getInitParameter("contextClass")是否有配置自定义容器
                     * 2.默认是没有配置 所以获得的是org.springframework.web.context.support.XmlWebApplicationContext 解析xml
                     */
                    this.context = this.createWebApplicationContext(servletContext);
                }
                /**
                 * 判断是否实现了ConfigurableWebApplicationContext
                  */
                if (this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                    //判断是否激活
                    if (!cwac.isActive()) {
                        //如果没有父节点 则获取sevletContext的rootjie
                        if (cwac.getParent() == null) {
                            /**
                             * <4>
                             * 这里会获取context-param key为locatorFactorySelector 配置的xml
                             * 同时获取析context-param key 为parentContextKey的bean 这个bean必须实现BeanFactory 和ApplicationContext 主要是我们自定义容器
                             * 并以这个容器作为root容器
                             */
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }
                        /**
                         * <3>
                         * 获取servetContext的initParameter key为:contextId值作为初始化容器的id
                         * 如果没有默认是使用org.springframework.web.context.WebApplicationContext:+servletContext.getContextPath()
                         * 解析xml执行初始化
                         */
                        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }
                /**
                 * 将容器设置到servletContext后续我们可以通过servleteContext获取到
                 */
                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.isDebugEnabled()) {
                    logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
                }

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

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

<2>createWebApplicationContext

快速返回:<1>

org.springframework.web.context.ContextLoader#initWebApplicationContext

->

org.springframework.web.context.ContextLoader#createWebApplicationContext

 protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//根据配置获取容器的类型 Class
<?> contextClass = this.determineContextClass(sc); /** * 判断contextClass 是否是ConfigurableWebApplicationContext类型或者子类型 * 就是是否能强转 */ if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } else { //创建对象 并强转为ConfigurableWebApplicationContext return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); } } protected Class<?> determineContextClass(ServletContext servletContext) { /** * 是否有配置自定义的context容器 * 在servletContext获取 key:contextClass */ String contextClassName = servletContext.getInitParameter("contextClass"); if (contextClassName != null) { try { //反射创建自定义配置 return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException var4) { throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4); } } else { /** * 获取默认配置 这里是XMLWebApplicationContext 从ContextLoader.properties 根据class名字获取配置 */ contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { //反射创建 return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException var5) { throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5); } } }

<3>configureAndRefreshWeb...

org.springframework.web.context.ContextLoader#initWebApplicationContext

->

org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext

 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        String configLocationParam;
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            configLocationParam = sc.getInitParameter("contextId");
            if (configLocationParam != null) {
                wac.setId(configLocationParam);
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        wac.setServletContext(sc);
        /**
         * 这里会获取到我们配置的spring xml地址
         * <context-param>
         *     <param-name>contextConfigLocation</param-name>
         *     <param-value>
         *       classpath:application-wemall.xml
         *     </param-value>
         *   </context-param>
         */
        configLocationParam = sc.getInitParameter("contextConfigLocation");
        if (configLocationParam != null) {
            //放到容器
            wac.setConfigLocation(configLocationParam);
        }

        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
        }

        this.customizeContext(sc, wac);
        /**
         *执行容器初始化
         */
        wac.refresh();
    }

 

<4>loadParentContext

快速返回:<1>

org.springframework.web.context.ContextLoader#initWebApplicationContext

->

org.springframework.web.context.ContextLoader#loadParentContext

protected ApplicationContext loadParentContext(ServletContext servletContext) {
        ApplicationContext parentContext = null;
        /**
         * 获取key为locatorFactorySelector   存储了spring xml地址 如:springContext.xml
         */
        String locatorFactorySelector = servletContext.getInitParameter("locatorFactorySelector");
        /**
         * 配置里面容器的bean id
         */
        String parentContextKey = servletContext.getInitParameter("parentContextKey");
        /**
         * 下面会解析配置的xml 并获取容器
         */
        if (parentContextKey != null) {
            BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
            Log logger = LogFactory.getLog(ContextLoader.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Getting parent context definition: using parent context key of '" + parentContextKey + "' with BeanFactoryLocator");
            }

            this.parentContextRef = locator.useBeanFactory(parentContextKey);
            parentContext = (ApplicationContext)this.parentContextRef.getFactory();
        }

        return parentContext;
    }

 

text.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="remoteOssClient" class="com.bozhi.wemall.SimepleBeanFactory">
    </bean>
</beans>

web.xml

<context-param>
    <param-name>locatorFactorySelector</param-name>
    <param-value>classpath:test.xml</param-value>
  </context-param>
  <context-param>
    <param-name>parentContextKey</param-name>
    <param-value>remoteOssClient</param-value>
  </context-param>

总结

1.root WebApplictonContext由servlet Listener ContextLoaderListener 进行初始化

2.默认是通过XmlWebApplicationContext作为容器 我们可以通过再ServletContext设置key为contextClass的Attribute指定容器class全名称 必须实现ConfigurableWebApplicationContext

2.初始化完成后会放置到servlteContext Attribute 后续我们可以获取

3.我们可以通过配置context param locatorFactorySelector  parentContextKey 自定义root容器

4.后续Servlet的初始化的容器的parent 则指向root

posted @ 2020-01-14 13:32  意犹未尽  阅读(687)  评论(0编辑  收藏  举报