• 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容器

  • 相关阅读:
    [TJOI2015]棋盘
    [FJOI2017]矩阵填数——容斥
    [ZJOI2016]小星星
    [HEOI2013]SAO ——计数问题
    ZJOI2008 骑士
    莫队算法——暴力出奇迹
    可持久化线段树
    dij与prim算法
    LCA 最近公共祖先
    Linux 设置交换分区
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12191552.html
Copyright © 2020-2023  润新知