SpringIOC初始化過程源碼跟蹤及學習

Spring版本 4.3.2,ssm框架java

代碼過寬,能夠shift + 鼠標滾輪 左右滑動查看web

web.xml

<!--配置獲取項目的根路徑,java類中使用System.getProperty("web.root")-->
<context-param>
    <param-name>webAppRootKey</param-name>
    <param-value>web.root</param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.util.WebAppRootListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

IOC容器初始化從ContextLoaderListener類開始,這個監聽器是ServletContextListener的子類,在web.xml配置後會被容器(爲避免和IOC容器混淆,後文用tomcat代稱)調用。該類繼承關係以下:spring

ContextLoaderListener繼承圖.png

tomcat初始化ContextLoaderListener類時會先調用其父類ContextLoader的靜態代碼塊數據庫

static {

    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    // 加載屬性文件默認策略的實現,當前處於徹底內部環境,不能由開發者去自定義
    try {

        // DEFAULT_STRATEGIES_PATH 常量,指向該類同一目錄層級下的ContextLoader.properties文件
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);

        //用流讀取文件中的key-value對,在這個地方肯定默認上下文的Class
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
    }
}

ContextLoader.properties文件中內容:數組

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

默認策略讀取完成,此策略肯定未指定上下文時的默認類型,爲XmlWebApplicationContext。tomcat

若是web.xml中有配置WebAppRootListener監聽器,根據web.xml中的順序,先調用WebAppRootListener的contextInitialized方法。app

public void contextInitialized(ServletContextEvent event) {
    WebUtils.setWebAppRootSystemProperty(event.getServletContext());
}

public static void setWebAppRootSystemProperty(ServletContext servletContext) throws IllegalStateException {
    Assert.notNull(servletContext, "ServletContext must not be null");

    //獲取項目在主機上的完整路徑root
    String root = servletContext.getRealPath("/");
    if (root == null) {
        throw new IllegalStateException(
            "Cannot set web app root system property when WAR file is not expanded");
    }

    //WEB_APP_ROOT_KEY_PARAM爲常量:webAppRootKey。在web.xml中獲取key爲webAppRootKey的value值
    String param = servletContext.getInitParameter(WEB_APP_ROOT_KEY_PARAM);

    //DEFAULT_WEB_APP_ROOT_KEY爲常量:webapp.root,如沒有指定WEB_APP_ROOT_KEY_PARAM則取此常量
    String key = (param != null ? param : DEFAULT_WEB_APP_ROOT_KEY);

    //無論拿的是DEFAULT_WEB_APP_ROOT_KEY仍是WEB_APP_ROOT_KEY_PARAM,以其value做爲key,找到對應的root
    String oldValue = System.getProperty(key);

    //確保root惟一
    if (oldValue != null && !StringUtils.pathEquals(oldValue, root)) {
        throw new IllegalStateException(
            "Web app root system property already set to different value: '" +
            key + "' = [" + oldValue + "] instead of [" + root + "] - " +
            "Choose unique values for the 'webAppRootKey' context-param in your web.xml files!");
    }

    //設置root
    System.setProperty(key, root);
    servletContext.log("Set web app root system property: '" + key + "' = [" + root + "]");
}

WebAppRootListener監聽器初始化任務結束,而後調用ContextLoaderListener監聽器的contextInitialized方法框架

/**
* Initialize the root web application context.
*
* 初始化根上下文
*/
@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}

initWebApplicationContext(零 )

initWebApplicationContext方法在ContextLoaderListener的父類ContextLoader中webapp

/**
	 * Initialize Spring's web application context for the given servlet context,
	 * using the application context provided at construction time, or creating a new one
	 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
	 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
	 * 
	 * 經過給定的 servlet context 初始化 Spring 的 web application context,
     * 這個 application context 要麼是在構造時被提供,要麼是根據 contextClass 
     * 和 contextConfigLocation 兩個上下文參數從新建立的。
	 */
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

    // servlet context 中只能有一個 root web application context。
    // root web application context 初始化完成後會放入 servlet context 中
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {

        // 不能初始化 context ,由於已經有一個 root web application context存在
        // 檢查你是否在你的web.xml中定義了多個ContextLoader
        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);

    // 初始化 spring root WebApplicationContext
    servletContext.log("Initializing Spring root WebApplicationContext");

    if (logger.isInfoEnabled()) {

        // Root WebApplicationContext 初始化開始
        logger.info("Root WebApplicationContext: initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {

        // Store context in local instance variable, to guarantee that
        // it is available on ServletContext shutdown.
        // 在這個實例變量中保存 context,
        // 以確保在 ServletContext 關閉時可以用到
        if (this.context == null) {
            // 1.建立 WebApplicationContext
            this.context = createWebApplicationContext(servletContext);
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;

            // 默認 WebApplicationContext 沒有激活               
            if (!cwac.isActive()) {

                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                // 此時 context 尚未被刷新 -> 提供設置 parent context 、application context id
                // 等服務。
                if (cwac.getParent() == null) {

                    // The context instance was injected without an explicit parent ->
                    // determine parent for root web application context, if any.
                    // 2.這個 context 實例被注入時沒有一個明確的 parent -> 若是有的話,
                    // 須要爲 root web application context 肯定 parent
                    ApplicationContext parent = loadParentContext(servletContext);
                    cwac.setParent(parent);
                }

                // 3.配置並刷新 WebApplicationContext(超級巨大的方法)
                configureAndRefreshWebApplicationContext(cwac, servletContext);
            }
        }

        //將 root web application context 添加到 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;
    }
}

1.createWebApplicationContext

從標1的方法開始,這個方法用來建立 root web application context ,並用ContextLoader的context變量接收ide

// 1.建立 WebApplicationContext
this.context = createWebApplicationContext(servletContext);

// 看下ContextLoader類的context屬性
/**
* The root WebApplicationContext instance that this loader manages.
*
* 由ContextLoader管理的 root web application context.
*/
private WebApplicationContext context;	



/**
* Instantiate the root WebApplicationContext for this loader, either the
* default context class or a custom context class if specified.
* <p>This implementation expects custom contexts to implement the
* {@link ConfigurableWebApplicationContext} interface.
* Can be overridden in subclasses.
* <p>In addition, {@link #customizeContext} gets called prior to refreshing the
* context, allowing subclasses to perform custom modifications to the context.
*
* 爲這個 loader 實例化 root WebApplicationContext ,
* 要用採用默認的 context class,要麼使用指定的自定義的 context class。
* 對於自定義的 contexts class 來講,
* 但願實現 ConfigurableWebApplicationContext 接口,也能夠重寫他的子類。
* 另外,customizeContext方法 會在 context 被刷新前調用,
* 容許子類執行對 context 的自定義修改。
*/
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {

    // 肯定 Context Class
    Class<?> contextClass = determineContextClass(sc);

    // 確保自定義 context 是 ConfigurableWebApplicationContext 的子類
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                                              "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }

    // 實例化 root WebApplicationContext
    return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}


/**
* Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified.
* 
* 返回 WebApplicationContext 接口的實現,
* 要麼使用默認的 XmlWebApplicationContext ,要麼使用自定義的 context
*/
protected Class<?> determineContextClass(ServletContext servletContext) {

    // CONTEXT_CLASS_PARAM -> contextClass  
    // 若是web.xml中配置了contextClass這個屬性,那麼就取自定義context Class
    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 {

        // 若是沒有配置自定義 context Class,
        // 則取默認策略中的 context Class,也就是 XmlWebApplicationContext
        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);
        }
    }
}

這樣標1的方法也就執行完了。看一下XmlWebApplicationContext的繼承關係:

XmlWebApplicationContext繼承圖.png

2.loadParentContext

跟蹤標記2的方法。

此方法在ContextLoader類中實現。

// 2.這個 context 實例被注入時沒有一個明確的 parent -> 若是有的話,
// 須要爲 root web application context 肯定 parent
ApplicationContext parent = loadParentContext(servletContext);


/**
* Template method with default implementation (which may be overridden by a
* subclass), to load or obtain an ApplicationContext instance which will be
* used as the parent context of the root WebApplicationContext. If the
* return value from the method is null, no parent context is set.
* <p>The main reason to load a parent context here is to allow multiple root
* web application contexts to all be children of a shared EAR context, or
* alternately to also share the same parent context that is visible to
* EJBs. For pure web applications, there is usually no need to worry about
* having a parent context to the root web application context.
* <p>The default implementation uses
* {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator},
* configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and
* {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context
* which will be shared by all other users of ContextsingletonBeanFactoryLocator
* which also use the same configuration parameters.
*
* 一個默認實現的模板方法(可能被子類覆蓋),
* 用來加載和得到一個 ApplicationContext 的實例,這個實例被用來
* 做爲 root WebApplicationContext 的 parent context 。
* 若是此方法返回的是null,那麼 root WebApplicationContext 就沒有 parent context 。
* 加載 parent context 的主要緣由是爲了讓多個 root web application contexts 
* 成爲共享 EAR context 的 children,
* 或者共享一個對 EJBs 可見的 parent context。
* 爲了web applications 的純潔性,一般不須要關心 root web application context 的 parent context。
* 默認實現使用 ContextSingletonBeanFactoryLocator,經過兩個參數去配置,去加載一個被全部
* ContextsingletonBeanFactoryLocator 其餘用戶所共享的 parent context,它們也使用同樣的配置參數。
*/
protected ApplicationContext loadParentContext(ServletContext servletContext) {
    ApplicationContext parentContext = null;
    String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
    String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);

    // 這裏沒有配置這兩個參數,因此此 context 沒有 parent
    if (parentContextKey != null) {

        // locatorFactorySelector may be null, indicating the default "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;
}

3.configureAndRefreshWebApplicationContext

跟蹤標記3的方法。

此方法在ContextLoader類中實現。

// 3.配置並刷新 WebApplicationContext(超級巨大的方法)
configureAndRefreshWebApplicationContext(cwac, servletContext);



protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {

    // 在類AbstractApplicationContext中,定義了 context 的id
    // private String id = ObjectUtils.identityToString(this);
    // id是 context 的惟一標識
    // identityToString 方法返回的是 context 的類名 + "@" + 十六進制的哈希值
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {

        // The application context id is still set to its original default value
        // -> assign a more useful id based on available information
        // 這個 application context id 仍然設置爲他的初始默認值-->基於可利用的信息分配給他一個更有用的id
        
        // 若是 servlet context 中設置了 CONTEXT_ID_PARAM -> contextId屬性,
        // 那麼就採用這個做爲id
        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
        if (idParam != null) {
            wac.setId(idParam);
        }
        else {

            // Generate default id...
            // 不然生成默認的id 
            // "org.springframework.web.context.WebApplicationContext:" + 項目名
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                      ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    wac.setServletContext(sc);

    // CONFIG_LOCATION_PARAM -> contextConfigLocation,
    // 從sevletContext中拿的這個屬性,就是咱們常常在web.xml中配置的屬性:				
    // classpath:spring/applicationContext.xml
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);

    if (configLocationParam != null) {

        // 以數組的形式將 contextConfigLocation 保存在
        // root web application context 的 configLocations 屬性中,
        // 保存時會先對路徑進行解析,替換佔位符,而這個操做須要 Environment 對象來完成。
        // context 中有屬性 environment,environment 默認爲 StandardServletEnvironment 實現,
        // 實例化 StandardServletEnvironment 時其父類 AbstractEnvironment 
        // 會調用 customizePropertySources 方法,
        // 這個方法會將 systemEnvironment、systemProperties 啥的鍵值對以及jndiProperties保存在實例中,
        // 後續還會將 servletContextInitParams 、servletConfigInitParams 等屬性保存進來
        wac.setConfigLocation(configLocationParam);
    } 

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    // 當 context 被刷新時,wac 的 environment 的 initPropertySources 方法在任何狀況下都會被調用。
    // 之因此這麼急切的調用是爲了確保 servlet 屬性源在一些 post-processing 中或者發生在refresh方法
    // 以前的初始化中可以到位使用。
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {

        //將 servletContextInitParams、servletConfigInitParams 等屬性保存進 environment 對象中
        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    }

    // 3.1對 Context 的自定義操做
    customizeContext(sc, wac);

    // 3.2整個IOC的重點,內容很是多,裏面的每一個方法都會分一篇文章單獨講
    wac.refresh();
}

3.1 customizeContext

跟蹤標記3.1的方法。

此方法在ContextLoader類中實現。

// 3.1對上下文的自定義操做
customizeContext(sc, wac);

/**
* Customize the {@link ConfigurableWebApplicationContext} created by this
* ContextLoader after config locations have been supplied to the context
* but before the context is <em>refreshed</em>.
* <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
* determines} what (if any) context initializer classes have been specified through
* {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
* given web application context.
* <p>Any {@code ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or marked with @{@link
* org.springframework.core.annotation.Order Order} will be sorted appropriately.
* 
* 當 config locations 被提供給 context 後,
* 由此 ContextLoader 建立的 ConfigurableWebApplicationContext 能夠進行自定義化,
* 可是必須在 context 被刷新以前進行。
* 這個默認的實現,determineContextInitializerClasses 方法,
* 經過兩個參數以及給定的 web application context 肯定了 context initializer classes,這兩個參數分別是
* CONTEXT_INITIALIZER_CLASSES_PARAM(上下文初始化參數)和ApplicationContextInitializer(調用每個)。
* 任何實現了 Ordered 或者有 Order 註解的 ApplicationContextInitializers 都將被適當的排序
*/
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {

    // 3.1.1web.xml中沒有自定義參數,因此此處返回空list
    List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
        determineContextInitializerClasses(sc);

    // 沒有自定義操做,因此跳過。有則實例化 Initializer
    for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
        Class<?> initializerContextClass =
            GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
        if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
            throw new ApplicationContextException(String.format(
                "Could not apply context initializer [%s] since its generic parameter [%s] " +
                "is not assignable from the type of application context used by this " +
                "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
                wac.getClass().getName()));
        }
        this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
    }

    // 若是有多個 contextInitializers,會根據 Order 作一個排序
    AnnotationAwareOrderComparator.sort(this.contextInitializers);
    for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {

        // contextInitializers 對 context 進行初始化
        initializer.initialize(wac);
    }
}
3.1.1 determineContextInitializerClasses

跟蹤標記3.1.1的方法。

此方法在ContextLoader類中實現。

// 3.1.1web.xml中沒有定義,因此此處返回空list
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
    determineContextInitializerClasses(sc);


/**
* Return the {@link ApplicationContextInitializer} implementation classes to use
* if any have been specified by {@link #CONTEXT_INITIALIZER_CLASSES_PARAM}.
* 
* 若是指定了 CONTEXT_INITIALIZER_CLASSES_PARAM 參數,
* 將會返回 ApplicationContextInitializer 接口的實現類的Class對象以供使用
*/
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
			determineContextInitializerClasses(ServletContext servletContext) {

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


	    //是否有自定義GLOBAL_INITIALIZER_CLASSES_PARAM參數,沒有跳過
		String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
		if (globalClassNames != null) {
			for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
				classes.add(loadInitializerClass(className));
			}
		}

		//是否有自定義CONTEXT_INITIALIZER_CLASSES_PARAM參數,沒有跳過
		String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
		if (localClassNames != null) {
			for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
				classes.add(loadInitializerClass(className));
			}
		}

		//web.xml中沒有定義,因此此處返回空list
		return classes;
	}

3.2 refresh

跟蹤標記3.2的方法。

此方法在AbstractApplicationContext類中實現。

// 3.2刷新操做
wac.refresh();

/**
* Load or refresh the persistent representation of the configuration,
* which might an XML file, properties file, or relational database schema.
* <p>As this is a startup method, it should destroy already created singletons
* if it fails, to avoid dangling resources. In other words, after invocation
* of that method, either all or no singletons at all should be instantiated.
* 
* 加載或者刷新配置的持久性表示,
* 這些配置可能在xml文件上,properties文件上,或者相關數據庫上。
* 由於這是一個啓動方法,因此若是他失敗的話那麼已經被建立的單例會被銷燬,避免佔用資源。
* 換句話說,在這個方法被調用以後,要麼全部單例都被實例化,要麼所有都沒有。
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

這上面的方法會一個個跟蹤。 未完····

參考: prepareRefresh方法:https://www.jianshu.com/p/c7364aeb0443 obtainFreshBeanFactory方法:https://www.jianshu.com/p/144af98965d9 prepareBeanFactory方法:https://www.jianshu.com/p/3468118a31f9 postProcessBeanFactory方法:https://www.jianshu.com/p/c05aea93b939

總結

  • ContextLoaderListener監聽器在web.xml中配置,是ServletContextListener子類,由容器調用
  • 父類靜態代碼塊讀取默認策略文件,肯定默認下ApplicationContext的類型
  • (零)開始初始化 web application context

——————————————————————————————————

  • (零)
  • 1.建立 web application context 。web.xml中有指定contextClass,則取自定義Context類型,須要實現ConfigurableWebApplicationContext接口,沒有指定則取默認策略類型
  • 2.有配置參數就實例化一個 ApplicationContext ,這個實例做爲 web application context 的 parent context
  • 3.配置並刷新 WebApplicationContext

——————————————————————————————————

  • 3
  • 配置 application context 的 id、contextConfigLocation等,這個過程會初始化Environment對象
  • 對 context 進行自定義操做,自定義初始化
  • 刷新 context ,核心十二個方法
相關文章
相關標籤/搜索