Spring4.3.x 容器在web應用中的初始化過程

概述

Spring在web應用中的默認容器類爲XmlWebApplicationContext,這個容器類通過xml文件獲取所有的配置信息。它的繼承結構如下圖,(點此查看大圖)
XmlWebApplicationContext繼承結構

在web應用中,不管是ContextLoaderListener,還是DispatcherServlet初始化的時候,都是以XmlWebApplicationContext爲默認容器。在下面的研究中,我將以ContextLoaderListener的初始化過程介紹spring容器在web應用的初始化。

ContextLoaderListener的初始化過程中最主要的任務時加載spring容器,並把此容器加入到ServletContext中作爲整個web應用的跟容器。ContextLoaderListener加載spring容器大致分爲兩個階段,第一個階段是解析web.xml文件中的初始化參數以對spring容器做定製化操作,簡單的說就是定製spring容器;第二階段是spring容器的刷新過程。下面分別對這兩個階段進行探討。

第一階段 定製spring容器

ContextLoaderListener實現了ServletContextListener,因此web容器啓動的時候就會執行它的contextInitialized方法,此方法的代碼如下。

    public void contextInitialized(ServletContextEvent event) {
        // 獲取ContextLoader對象
        this.contextLoader = createContextLoader();
        if (this.contextLoader == null) {
            this.contextLoader = this;
        }
        this.contextLoader.initWebApplicationContext(event.getServletContext());
    }
    @Override
    public void contextInitialized(ServletContextEvent event) {
        // 執行父類ContextLoader的initWebApplicationContext方法
        initWebApplicationContext(event.getServletContext());
    }

這段代碼主要是調用父類ContextLoader的initWebApplicationContext(ServletContext servletContext)方法,下面是initWebApplicationContext方法在ContextLoader類中的代碼。

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        // 檢查ServletContext是否已經有了根容器
        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!");
        }

        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) {
                // 創建容器,據contextClass初始化參數指定或者使用默認的XmlWebApplicationContext類
                this.context = createWebApplicationContext(servletContext);
            }
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                if (!cwac.isActive()) {
                    // 如果容器沒有被刷新,執行以下操作

                    // 設置父容器
                    if (cwac.getParent() == null) {
                        // 加載父容器。
                        // 通過locatorFactorySelector上下文初始化參數指定父容器所在的配置文件路徑
                        // 通過parentContextKey上下文初始化參數指定父容器的名稱
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    // 配置並執行容器刷新操作
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }

    // 把spring容器加入到ServletContext中作爲根容器
    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 ex) {
            logger.error("Context initialization failed", ex);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
            throw ex;
        } catch (Error err) {
            logger.error("Context initialization failed", err);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
            throw err;
        }
    }

這段代碼分成4步,首先通過調用ContextLoader的createWebApplicationContext(ServletContext sc)方法來創建spring容器,然後設置spring容器的父容器,接着調用ContextLoader的configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)方法來配置並刷新spring容器,最後把容器保存到servlet容器中。最後一步的代碼已經在上面體現了,下面我們來解析前三步的代碼。

1. 創建spring容器。

調用ContextLoader的createWebApplicationContext(ServletContext sc)方法,這個方法的代碼如下。

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        // 獲取容器類對象
        Class<?> contextClass = determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
        // 使用容器類對象來實例化容器
        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

上面主要是通過ContextLoader的determineContextClass(ServletContext servletContext)方法獲取容器類對象,然後實例化容器。下面是determineContextClass方法的代碼。

    /**
    * 獲取容器類對象
    **/
    protected Class<?> determineContextClass(ServletContext servletContext) {
        // 聲明:public static final String CONTEXT_CLASS_PARAM = "contextClass";
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            } catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        } else {
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            } catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }

determineContextClass從ServletContext對象中獲取contextClass初始化參數的值,如果這個參數有值,則使用這個參數指定的容器類,否則使用默認的容器類XmlWebApplicationContext。如果不使用spring的默認容器,可以在web.xml中通過配置contextClass指定其他容器類,比如。

    <!-- 定義contextClass參數 -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            com.chyohn.context.XmlWebApplicationContext
        </param-value>
    </context-param>

2. 指定父容器。

創建完spring容器後,initWebApplicationContext中會調用ContextLoader的 loadParentContext(ServletContext servletContext)方法來獲取父容器,並把這個父容器與剛創建的容器關聯上。loadParentContext方法的代碼如下。

    protected ApplicationContext loadParentContext(ServletContext servletContext) {
        ApplicationContext parentContext = null;
        // 聲明:public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
        String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
        // 聲明:public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
        String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);

        if (parentContextKey != null) {
            // locatorFactorySelector可能會爲null, 則會使用默認的 "classpath*:beanRefContext.xml"
            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;
    }

這裏通過ServletContext 獲取初始化參數locatorFactorySelector指定的定義父容器的xml文件的地址,同時獲取初始化參數parentContextKey指定的父容器在前面xml文件中設置的bean名稱。下面是一個列子。

第一步在classes路徑下創建名爲parentBeanRefContext.xml的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-3.1.xsd">  
      <!-- 指定容器 -->
     <bean id="parentContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">    
         <constructor-arg>    
                <list>    
                     <value>parentOfRootContext.xml</value>    
                </list>    
         </constructor-arg>    
     </bean>

</beans>   

其中parentOfRootContext.xml文件爲一個普通的spring配置文件,這裏就不舉例了。

第二步,在web.xml文件中做如下配置。

    <!-- 定義locatorFactorySelector參數 -->
    <context-param>
        <param-name>locatorFactorySelector</param-name>
        <param-value>
            classpath:parentBeanRefContext.xml
        </param-value>
    </context-param>
    <!-- 定義parentContextKey參數 -->
    <context-param>
        <param-name>parentContextKey</param-name>
        <param-value>parentContext</param-value>
    </context-param>

這樣就向容器中指定了一個父容器。在這裏如果在第一步中創建的xml文件的名稱爲beanRefContext.xml,那麼在web.xml文件中就不用配置locatorFactorySelector參數。

3. 配置並刷新spring容器

設置了父容器後,執行ContextLoader的configureAndRefreshWebApplicationContext方法,在容器刷新前對容器進行初始化配置,代碼如下

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // 爲容器設置一個有用的ID
            // 聲明:public static final String CONTEXT_ID_PARAM = "contextId";
            String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
            if (idParam != null) {
                wac.setId(idParam);
            } else {
                // 生成一個默認ID
                if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
                    // servlet 2.5以前的版本
                    wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                            ObjectUtils.getDisplayString(sc.getServletContextName()));
                } else {
                    // servlet 2.5及其以上的版本wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                            ObjectUtils.getDisplayString(sc.getContextPath()));
                }
            }
        }

        // 把ServletContext保存到容器中
        wac.setServletContext(sc);
        // 設置容器要加載的配置文件所在的路徑
        // 通過contextConfigLocation上下文參數指定配置文件路徑
        // 聲明:public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
        String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
        if (initParameter != null) {
            wac.setConfigLocation(initParameter);
        }
        // 在容器刷新前,自定義容器。
        // 執行用戶通過contextInitializerClasses上下文參數指定的容器初始化器
        customizeContext(sc, wac);

        // 刷新容器
        wac.refresh();
    }
    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // 爲容器設置一個有用的ID
            // 聲明:public static final String CONTEXT_ID_PARAM = "contextId";
            String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
            if (idParam != null) {
                wac.setId(idParam);
            } else {
                // 創建一個默認的id
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        // 把ServletContext保存到容器中
        wac.setServletContext(sc);
        // 設置容器要加載的配置文件所在的路徑
        // 通過contextConfigLocation上下文參數指定配置文件路徑
        // 聲明:public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
        String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
        if (configLocationParam != null) {
            wac.setConfigLocation(configLocationParam);
        }

        // 提前執行容器環境對象的initPropertySources方法,以確保servlet屬性資源可應用於容器刷新前的任何初始化操作。
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
        }

        // 在容器刷新前,自定義容器。
        // 執行用戶通過contextInitializerClasses上下文參數指定的容器初始化器
        customizeContext(sc, wac);

        // 刷新容器
        wac.refresh();
    }

這段代碼處理配置在web.xml中的2個初始化參數,第一個是用於標誌容器id的contextId初始化參數,第二是用於指定配置文件地址的contextConfigLocation初始化參數。對於contextId參數沒有過多的探討,至於contextConfigLocation參數,可以配置,也可以不配置。如果需要通過contextConfigLocation參數指定多個配置文件,配置文件地址之間可以通過英文逗號、分號、空格、製表符、換行符隔開。如果沒有配置contextConfigLocation參數,XmlWebApplicationContext將使用WEB-INF目錄下的默認配置文件地址,代碼如下。

    /** Default config location for the root context */
    public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

    /** Default prefix for building a config location for a namespace */
    public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";

    /** Default suffix for building a config location for a namespace */
    public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";

    @Override
    protected String[] getDefaultConfigLocations() {
        if (getNamespace() != null) {
            return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
        }
        else {
            // 返回配置地址/WEB-INF/applicationContext.xml
            return new String[] {DEFAULT_CONFIG_LOCATION};
        }
    }

根據這段代碼,可以獲得兩個信息。
其一:如果spring容器有命名空間,則開發者可以在WEB-INF目錄下創建以命名空間爲名稱的xml配置文件。
其二:如果spring容器沒有命名空間,則開發者可以在WEB-INF目錄下創建以applicationContext爲名稱的xml配置文件。

在configureAndRefreshWebApplicationContext方法中還調用ContextLoader的customizeContext方法來執行用戶指定ApplicationContextInitializer對象,下面是customizeContext方法的代碼。

    protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {

        // 獲取ApplicationContextInitializer類對象列表
        List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = determineContextInitializerClasses(servletContext);

        if (initializerClasses.size() == 0) {
            // 沒有指定任何 ApplicationContextInitializers對象,則什麼都不做,直接返回
            return;
        }

        Class<?> contextClass = applicationContext.getClass();
        ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
                new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();

        for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
            // 從initializerClass對象獲取ApplicationContextInitializer的泛型類對象
            Class<?> initializerContextClass =
                    GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
            // 檢查contextClass是否是initializerContextClass類對象指定的類的實現
            Assert.isAssignable(initializerContextClass, contextClass, String.format(
                    "Could not add context initializer [%s] as its generic parameter [%s] " +
                    "is not assignable from the type of application context used by this " +
                    "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
                    contextClass.getName()));
            initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
        }

        ConfigurableEnvironment env = applicationContext.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            // 初始化Property資源
            ((ConfigurableWebEnvironment)env).initPropertySources(servletContext, null);
        }

        // 一個一個的執行ApplicationContextInitializer對象
        Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
        for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
            initializer.initialize(applicationContext);
        }
    }

這段代碼主要是調用ContextLoader對象的determineContextInitializerClasses方法來獲取ApplicationContextInitializer類對象列表,並使用每個ApplicationContextInitializer對象來對spring容器做更多的初始化操作。下面是determineContextInitializerClasses的代碼。

    protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
            determineContextInitializerClasses(ServletContext servletContext) {

        List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
                new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();

        // 從ServletContext中獲取應用於所有spring web應用容器的ApplicationContextInitializer實現類全名稱
        // 聲明有:public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
        String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
        if (globalClassNames != null) {
            for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
                classes.add(loadInitializerClass(className));
            }
        }


        // 從ServletContext中獲取專爲爲此容器指定的ApplicationContextInitializer實現類全名稱
        // 聲明:public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
        String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
        if (localClassNames != null) {
            for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
                classes.add(loadInitializerClass(className));
            }
        }

        return classes;
    }

    private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) {
        try {
            Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
            Assert.isAssignable(ApplicationContextInitializer.class, clazz);
            return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz;
        } catch (ClassNotFoundException ex) {
            throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
        }
    }

determineContextInitializerClasses方法從ServletContext中獲取初始化參數contextInitializerClasses和globalInitializerClasses的值,這個值指定了用戶自定義的ApplicationContextInitializer實現類全名稱。然後根據類的全名稱創建Class對象。其中如果需要指定多個ApplicationContextInitializer實現類,那麼實現類的全名稱之間使用英文逗號隔開,比如下面的配置。

    <!-- 定義globalInitializerClasses參數 -->
    <context-param>
        <param-name>globalInitializerClasses</param-name>
        <param-value>
            com.damuzee.web.app.GlobalApplicationContextInitializer1,
            com.damuzee.web.app.GlobalApplicationContextInitializer2,
            com.damuzee.web.app.GlobalApplicationContextInitializer3
        </param-value>
    </context-param>

    <!-- 定義contextInitializerClasses參數 -->
    <context-param>
        <param-name>contextInitializerClasses</param-name>
        <param-value>
            com.damuzee.web.app.XmlApplicationContextInitializer1,
            com.damuzee.web.app.XmlApplicationContextInitializer2,
            com.damuzee.web.app.XmlApplicationContextInitializer3
        </param-value>
    </context-param>

到此,在web應用中spring容器初始化的第一個階段就完成了。configureAndRefreshWebApplicationContext方法通過調用容器的refresh()方法進入容器初始化的第二階段——容器的刷新過程

第二階段: 容器的刷新過程

關於spring容器的刷新過程已經在另一篇文章中描述了,詳見 Spring ApplicationContext的刷新過程

總結

在spring容器初始化的第一個階段,我們可以通過web.xml文件的配置來定製spring容器。通過web.xml文件,我們可以指定其他容器類、父容器、容器的id、需要加載的配置文件地址、以及自定義容器初始化器ApplicationContextInitializer對象。具體例子如下。

  • 通過設置contextClass參數指定容器,例如
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            com.chyohn.context.XmlWebApplicationContext
        </param-value>
    </context-param>
  • 設置locatorFactorySelector和parentContextKey參數指定父容器,例如
    <!-- 定義locatorFactorySelector參數 -->
    <context-param>
        <param-name>locatorFactorySelector</param-name>
        <param-value>
            classpath:parentBeanRefContext.xml
        </param-value>
    </context-param>
    <!-- 定義parentContextKey參數 -->
    <context-param>
        <param-name>parentContextKey</param-name>
        <param-value>parentContext</param-value>
    </context-param>

上面的parentBeanRefContext.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-3.1.xsd">  
      <!-- 指定容器 -->
     <bean id="parentContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">    
         <constructor-arg>    
                <list>    
                     <value>parentOfRootContext.xml</value>    
                </list>    
         </constructor-arg>    
     </bean>

</beans>    
  • 設置contextId參數指定容器的Id,配置如下。
    <context-param>
        <param-name>contextId</param-name>
        <param-value>myContextId</param-value>
    </context-param>
  • 設置contextConfigLocation參數指定加載的配置文件地址,配置如下。
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:webApplicationContent.xml
            classpath:application-service.xml
        </param-value>
    </context-param>
  • 設置contextInitializerClasses參數指定容器初始化器,該初始化器將在容器刷新前執行,如果有多個初始化器,使用英文逗號“,”隔開,例如
    <!-- 定義contextInitializerClasses參數 -->
    <context-param>
        <param-name>contextInitializerClasses</param-name>
        <param-value>
            com.damuzee.web.app.XmlApplicationContextInitializer1,
            com.damuzee.web.app.XmlApplicationContextInitializer2,
            com.damuzee.web.app.XmlApplicationContextInitializer3
        </param-value>
    </context-param>
  • 設置globalInitializerClasses參數指定所有容器公共的初始化器,該初始化器將在容器刷新前執行,如果有多個初始化器,使用英文逗號“,”隔開,例如
    <!-- 定義globalInitializerClasses參數 -->
    <context-param>
        <param-name>globalInitializerClasses</param-name>
        <param-value>
            com.damuzee.web.app.GlobalApplicationContextInitializer1,
            com.damuzee.web.app.GlobalApplicationContextInitializer2,
            com.damuzee.web.app.GlobalApplicationContextInitializer3
        </param-value>
    </context-param>

有了ApplicationContextInitializer對象,可以對容器的初始化做更多操作,比如設置容器id、設置父容器、設置加載的配置文件地址、添加容器級的bean工廠後處理器、監聽器等等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章