zoukankan      html  css  js  c++  java
  • 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容器

  • 相关阅读:
    cinder支持nfs快照
    浏览器输入URL到返回页面的全过程
    按需制作最小的本地yum源
    创建可执行bin安装文件
    RPCVersionCapError: Requested message version, 4.17 is incompatible. It needs to be equal in major version and less than or equal in minor version as the specified version cap 4.11.
    惠普IPMI登陆不上
    Linux进程状态——top,ps中看到进程状态D,S,Z的含义
    openstack-neutron基本的网络类型以及分析
    openstack octavia的实现与分析(二)原理,架构与基本流程
    flask上下文流程图
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12191552.html
Copyright © 2011-2022 走看看