Spring原始碼系列-容器重新整理

glmapper發表於2017-12-24

Spring對於程式設計師說來說都不陌生;作為一個強大的開源技術,幫助我們能夠更好的進行專案的開發與維護。

上次在Spring的啟動過程文章中對Spring的啟動過程做了一個較為詳細的說明和分析。那麼在實際的過程中,Spring的啟動實際上就是Spring容器的初始化過程。本文將從原始碼的角度結合自己斷點執行過程中保留的現場來分析一下容器的重新整理過程(主要分析前幾個方法,後面幾個會分開來說)。

Spring的啟動是通過ContextLoaderListener來進行的,在ContextLoaderListener中通過委託父類ContextLoader的initWebApplicationContext來完成具體的初始化過程。具體的啟動過程可以看下之前的那篇文章。

在initWebApplicationContext方法是用來建立容器的,核心程式碼如下:

Spring原始碼系列-容器重新整理
今天主要來看configureAndRefreshWebApplicationContext方法中最後的wac.refresh()到底發生了哪些事;

Spring原始碼系列-容器重新整理

1、obtainFreshBeanFactory:BeanFactory的重新整理和建立

refresh()方法主要為IoC容器Bean的生命週期管理提供條件,Spring IoC容器載入Bean定義資原始檔從其子類容器的refreshBeanFactory()方法啟動,所以整個refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”這句以後程式碼的都是註冊容器的資訊源和生命週期事件,載入過程就是從這句程式碼啟動。 AbstractApplicationContext的obtainFreshBeanFactory()方法呼叫子類容器的refreshBeanFactory()方法,啟動容器載入Bean定義資原始檔的過程。 refresh()方法的作用是:在建立IoC容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器。refresh的作用類似於對IoC容器的重啟,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入。 和refreshBeanFactory方法類似,載入Bean定義的方法loadBeanDefinitions也使用了委派模式,在AbstractRefreshableApplicationContext類中只定義了抽象方法,具體的實現呼叫子類容器中的方法實現。

//通知子類去重新整理內部bean 工廠

Spring原始碼系列-容器重新整理
再來看refreshBeanFactory

此實現執行該上下文的底層bean工廠的實際重新整理,關閉以前的bean工廠(如果有的話),併為上下文生命週期的下一階段初始化一個新的bean工廠。

Spring原始碼系列-容器重新整理

customizeBeanFactory(DefaultListableBeanFactory beanFactory)

/**
        //通過當前上下文來自定義內部bean工廠<br>
	 * Customize the internal bean factory used by this context.
	 * Called for each {@link #refresh()} attempt.
	 * <p>The default implementation applies this context's
	 * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
	 * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
	 * if specified. Can be overridden in subclasses to customize any of
	 * {@link DefaultListableBeanFactory}'s settings.
	 * @param beanFactory the newly created bean factory for this context
	 * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
	 * @see DefaultListableBeanFactory#setAllowCircularReferences
	 * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
	 * @see DefaultListableBeanFactory#setAllowEagerClassLoading
	 */
	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}
複製程式碼

XmlWebApplicationContext類中loadBeanDefinitions(beanFactory)

@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}
複製程式碼

AbstractApplicationContext呼叫loadBeanDefinitions(DefaultListableBeanFactory beanFactory) ,此方法根據首先建立XmlBeanDefinitionReader物件,然後配置該物件的上下文和資源載入環境,同時呼叫子類實現的initBeanDefinitionReader對XmlBeanDefinitionReader進行個性化配置,最近後入到initBeanDefinitionReader(beanDefinitionReader)的呼叫:

  1. 據給定的BeanFactory建立XmlBeanDefinitionReader 物件
  2. 配置beanDefinitionReader的上下文和資源載入環境
  3. 用子類實現的initBeanDefinitionReader對XmlBeanDefinitionReader進行個性化配置initBeanDefinitionReader(beanDefinitionReader);
  4. 呼叫載入Bean定義的方法,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現呼叫子類容器

裝載bean定義通過XmlBeanDefinitionReader。

// Create a new XmlBeanDefinitionReader for the given BeanFactory. 通過給定的bean工廠建立一個新的XmlBeanDefinitionReader
1.XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

2.使用上下文的資源載入環境配置bean定義讀取器。
   beanDefinitionReader.setEnvironment(getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
3.允許子類提供reader的自定義初始化,然後繼續實際載入bean定義。
    //通過制定的XmlBeanDefinitionReader來載入beandefinitionReader 
   initBeanDefinitionReader(beanDefinitionReader)
   // 通過制定的XmlBeanDefinitionReader來載入bean definitions
   loadBeanDefinitions(beanDefinitionReader)
複製程式碼

AbstractApplicationContext呼叫loadBeanDefinitions(beanDefinitionReader),這個方法是取得資源或資源路徑然後通過傳入的reader去載入BeanDefinitions。

Spring原始碼系列-容器重新整理

2、loadBeanDefinitions

目前使用Spring的配置都是基於XML的,因此使用XmlBeanDefinitionReader 中的loadBeanDefinitions方法。

Spring原始碼系列-容器重新整理
Spring原始碼系列-容器重新整理
看doLoadBeanDefinitions,這個就是具體的讀取檔案配置,然後註冊成Bean

Spring原始碼系列-容器重新整理

Spring原始碼系列-容器重新整理

3、prepareBeanFactory

配置工廠的標準上下文特性,如上下文的類裝載器和後處理器。

Spring原始碼系列-容器重新整理

  • 告訴內部bean工廠使用上下文的類裝入器等。
  • 上下文回撥配置bean工廠。
  • BeanFactory介面未登記為普通工廠的解析式。MessageSource登記(為自動裝配建立)作為一個Bean
  • 如果建立;就去尋找LoadTimeWeaver,然後準備組織
  • 註冊預設環境bean。

通過斷點來看下當前的beanFactory

Spring原始碼系列-容器重新整理
Spring原始碼系列-容器重新整理
繼續執行...

Spring原始碼系列-容器重新整理
Spring原始碼系列-容器重新整理
beanDefinitionMap

Spring原始碼系列-容器重新整理
manualSingletonNames

Spring原始碼系列-容器重新整理

4、postProcessBeanFactory

註冊web特性的全域性域

1).registerWebApplicationScopes

Spring原始碼系列-容器重新整理
註冊具有web特性的域;包括:"request", "session", "globalSession", "application"

Spring原始碼系列-容器重新整理
看下儲存結構:
Spring原始碼系列-容器重新整理
registerScope方法
Spring原始碼系列-容器重新整理
2).registerEnvironmentBeans

註冊web特性 環境bean(“contextparameters”、“ContextAttribute”)與給定的WebApplicationContext使用BeanFactory。

1.servletContext

Spring原始碼系列-容器重新整理
2.servletConfig
Spring原始碼系列-容器重新整理
3.registerSingleton

Spring原始碼系列-容器重新整理

Spring原始碼系列-容器重新整理

Spring原始碼系列-容器重新整理
這裡是找到了我們預設的配置檔案引數:
Spring原始碼系列-容器重新整理
beanName=contextParameters

Spring原始碼系列-容器重新整理
Spring原始碼系列-容器重新整理
Spring原始碼系列-容器重新整理
最後是將contextAttributes放入;contextAttributes中包含的屬性值比較多,具體如下面所示:

Spring原始碼系列-容器重新整理
主要包括: javax.servlet.context.tempdir, org.apache.catalina.resources, org.springframework.web.context.support.ServletContextScope, org.apache.tomcat.util.scan.MergedWebXml, org.apache.tomcat.InstanceManager, org.apache.catalina.jsp_classpath, javax.websocket.server.ServerContainer, org.apache.tomcat.JarScanner

Spring原始碼系列-容器重新整理
這裡是把需要的東西全部載入進來了,有很多。就不貼了(mime-mapping)....

5、invokeBeanFactoryPostProcessors

BeanDefinitionRegistryPostProcessor例項化:標準BeanFactoryPostProcessor的擴充套件,BeanFactoryPostProcessor的作用是用來進一步定義註冊的BeanDefinition,IoC容器本質就是Bean管理,所以BeanFactoryPostProcessor本身也是Bean,要對BeanFactoryPostProcessor的BeanDefinition進一步定義就通過BeanDefinitionRegistryPostProcessor進行註冊,BeanDefinitionRegistryPostProcessor及其子類是Ioc容器最例項化的一類Bean。它們在ConfigurableApplicationContext(ApplicationContext子介面)實現類呼叫refresh()方法呼叫invokeBeanFactoryPostProcessors(beanFactory);方法時就被例項化。

OK,今天關於這部分的分析就到此結束了,後面的過程會在下一篇Spring系列文章中繼續來講refresh中的過程。

祝大家平安夜快樂....

如果您對系列文章有任何意見,可以給我留言,感謝大家。

下面是一個接蘋果的姿勢,呼呼呼.....

Spring原始碼系列-容器重新整理

相關文章