1. 程式人生 > 程式設計 >Spring IOC 容器啟動流程分析

Spring IOC 容器啟動流程分析

Spring IOC 容器啟動流程分析

使用 Spring 時,XML 和註解是使用得最多的兩種配置方式,雖然是兩種完全不同的配置方式,但對於 IOC 容器來說,兩種方式的不同主要是在 BeanDefinition 的解析上。而對於核心的容器啟動流程,仍然是一致的。

AbstractApplicationContextrefresh 方法實現了 IOC 容器啟動的主要邏輯,啟動流程中的關鍵步驟在原始碼中也可以對應到獨立的方法。接下來以 AbstractApplicationContext 的實現類 ClassPathXmlApplicationContext 為主 ,並對比其另一個實現類 AnnotationConfigApplicationContext

, 解讀 IOC 容器的啟動過程。

AbstractApplicationContext.refresh


	@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(); } // ... } } 複製程式碼

ApplicationContext 和 BeanFactory 的關係

ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext 的繼承樹如下所示。兩者都繼承自 AbstractApplicationContext

Spring ApplicationContext 繼承圖.jpg
ApplicationContext 繼承樹(高清大圖)

Spring BeanFactory 繼承圖.jpg
BeanFactory 繼承樹(高清大圖)

ApplicationContext 是 IOC 容器的承載體,而 BeanFactory 是操作這個容器的工具,兩者關係緊密,相互協作。refresh 方法實現了 ApplicationContextBeanFactory 相互協作的主要過程,不同之處主要在子類 AbstractRefreshableApplicationContextGenericApplicationContext 中實現,兩者使用的 BeanFactory 都為 DefaultListableBeanFactoryDefaultListableBeanFactory 的定義如下:

DefaultListableBeanFactory:

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory,BeanDefinitionRegistry,Serializable
複製程式碼

可見 DefaultListableBeanFactory 實現了 ConfigurableListableBeanFactory,意味著是可配置,可遍歷的,至於為什麼可以,讓我們繼續往下尋找找答案。

BeanDefinition 的獲取

DefaultListableBeanFactory 中使用 Map 結構儲存所有的 BeanDefinition 資訊:

DefaultListableBeanFactory:

private final Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
複製程式碼

ClassPathXmlApplicationContext 中的解析

使用 BeanDefinitionDocumentReader(可參看 DefaultBeanDefinitionDocumentReader.processBeanDefinition方法) 將 xml 中的 bean 解析為 BeanDefinition, 然後由 BeanDefinitionRegistry 註冊到 BeanFactory 中。 入口:AbstractApplicationContext.refreshBeanFactory(在 refresh 中呼叫)

AnnotationConfigApplicationContext 中的解析

通過 BeanDefinitionScanner 掃描 Bean 宣告,解析為 BeanDefinition 並由 BeanDefinitionRegistry 註冊到 BeanFactory 中。 入口:AnnotationConfigApplicationContext 的建構函式。

為什麼 ClassPathXmlApplicationContext 的入口是在 refreshBeanFactory 方法中 ?

AbstractApplicationContext.refreshBeanFactory 定義如下:

AbstractApplicationContext:

protected abstract void refreshBeanFactory() throws BeansException,IllegalStateException
複製程式碼

可見是一個抽象方法,具體實現在子類中。只有 "Refreshable" 的 BeanFactory 才會在該方法中實現具體操作,如 AbstractRefreshableApplicationContext:

AbstractRefreshableApplicationContext:

@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(),ex);
		}
	}
複製程式碼

可見 AbstractRefreshableApplicationContext.``refreshBeanFactory 方法會檢查 BeanFactory 是否已經存在(hasBeanFactory),已經存在就先銷燬所有的 Bean(destoryBeans)並關閉(closeBeanFactory) BeanFactory,然後再建立(createBeanFactory)新的。 而 GenericApplicationContext.refreshBeanFactory中會檢查是否為第一次呼叫,不是就丟擲異常,不執行其他邏輯,即 GenericApplicationContext 不是 "Refreshable"的。

主流程分析

refresh 方法在 AbstractApplicationContext 中定義,其中的 obtainFreshBeanFactory 方法呼叫了 getBeanFactory 方法,該方法用於獲取 BeanFactory,這裡為 DefaultListableBeanFactory,接下來無特別說明,大部分的方法和變數都將取自 AbstractApplicationContext 和  DefaultListableBeanFactory

Spring Context refresh 主流程時序圖_看圖王.jpg
高清大圖

BeanPostProcessor

BeanPostProcessor 介面讓開發者在 IOC 容器對 Bean 進行例項化時收到回撥(postProcessAfterInitialization 和 postProcessBeforeInitialization 方法)。spring 框架內部的許多通知(Aware)就是通過這個介面實現,如 ApplicationContextAwareProcessorServletContextAwareProcessor,他們的實現會在 postProcessBeforeInitialization 方法中進行檢查,若實現了特定介面,就會呼叫 Aware 的回撥方法,給予通知:

ServletContextAwareProcessor:

@Override
	public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException {
		if (getServletContext() != null && bean instanceof ServletContextAware) {
			((ServletContextAware) bean).setServletContext(getServletContext());
		}
		if (getServletConfig() != null && bean instanceof ServletConfigAware) {
			((ServletConfigAware) bean).setServletConfig(getServletConfig());
		}
		return bean;
	}
複製程式碼

在 postProcessBeanFactory 方法中,子類可以通過 beanFactory.addBeanPostProcessor 方法新增自己的 BeanPostProcessorbeanFactory 中,最終將儲存到 BeanFactory 的 beanPostProcessors(實為CopyOnWriteArrayList) 中。prepareBeanFactoryregisterBeanPostProcessors 方法是集中例項化並新增這些 Bean 的地方。

BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor

從"BeanDefinition 的獲取"的介紹可以知道 BeanDefinitionRegistry 用於將  BeanDefinition 註冊到 BeanFactory 中,GenericApplicationContext 和 DefaultListableBeanFactory 都實現了該介面,GenericApplicationContext 中的實現直接呼叫了 beanFactory 的實現。

BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor 與 BeanPostProcessor 類似,但從他們的命名就可以看出,所針對的目標不同,分別是 BeanFactory 和 BeanDefinitionRegistry: 1 BeanFactoryPostProcessor 回撥讓開發者有機會在 BeanFactory 已經初始化好的情況下對 BeanFactory 的一些屬性進行覆蓋,或是對 beanDefinitionMap 中的 BeanDefinition 進行修改。 2 BeanDefinitionRegistryPostProcessor 則讓開發者可以繼續新增 BeanDefinition 到 BeanFactory 中。

具體邏輯在 invokeBeanFactoryPostProcessors 中實現,這裡首先會將所有實現了 BeanFactoryPostProcessors的 Bean 例項化,然後呼叫其回撥方法(postProcessBeanDefinitionRegistry 或 postProcessBeanFactory 方法)。

對於這部分 Bean 的例項化和進行回撥有一定的優先順序規則。PriorityOrdered 繼承自 Ordered 介面,實現了 PriorityOrdered 的 BeanDefinitionRegistryPostProcessor 將最先被例項化並呼叫,然後同樣的規則來回撥實現了 BeanFactoryPostProcessor 的 Bean: PriorityOrdered > Ordered > 未實現 Ordered 的

在 registerBeanPostProcessors 方法中對 BeanPostProcessor 的例項化也有這樣的優先順序規則: PriorityOrdered > Ordered > 未實現 Ordered 的 > MergedBeanDefinitionPostProcessor

ApplicationEventMulticaster

initApplicationEventMulticaster 中會對 ApplicationEventMulticaster 進行初始化: 首先會檢查是否已經有了ApplicationEventMulticaster 的 BeanDefinition(在 beanDefinitionMap 中檢查),有就讓容器進行例項化,沒有就使用框架預設的 ApplicationEventMulticaster (即 SimpleApplicationEventMulticaster),先例項化,然後註冊到容器中(MessageSourceinitMessageSource 方法中也是同樣的方式進行初始化)。

事件的起始傳送處將事件包裝為 ApplicationEvent ,並通過 ApplicationEventPublisher 提交給 ApplicationEventMulticasterApplicationEventMulticaster 會將事件廣播給 ApplicationListener,處理最終的分發。

AbstractApplicationEventMulticaster 中的 applicationListeners(實為 LinkedHashSet<ApplicationListener>) 變數儲存了所有的廣播接收者,registerListeners 方法會將所有的 ApplicationListener 新增到該集合中。

finishRefresh 方法中有一個對 ContextRefreshedEvent 事件的廣播可以作為參考,最終事件會由 multicastEvent 方法處理:

SimpleApplicationEventMulticaster.multicastEvent

@Override
	public void multicastEvent(final ApplicationEvent event,@Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event,type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener,event));
			}
			else {
				invokeListener(listener,event);
			}
		}
	}
複製程式碼

那麼在我們自己的 Bean 中如何得到這個 ApplicationEventPublisher 呢? ApplicationContext 的定義如下:

ApplicationContext:

public interface ApplicationContext extends EnvironmentCapable,ListableBeanFactory,HierarchicalBeanFactory,MessageSource,ApplicationEventPublisher,ResourcePatternResolver {
複製程式碼

可見 ApplicationContext 繼承了 ApplicationEventPublisher,這就說明 AbstractApplicationContext 也是一個 ApplicationEventPublisher。在我們自己的 Bean 中通過實現 ApplicationEventPublisherAware,我們就能通過 setApplicationEventPublisher 回撥得到 ApplicationEventPublisher

上面我們提到 spring 的許多 Aware 是通過 BeanPostProcessor 實現的,ApplicationEventPublisherAware 也不例外:

ApplicationContextAwareProcessor:

@Override
	@Nullable
	public Object postProcessBeforeInitialization(final Object bean,String beanName) throws BeansException {
		// ...
        if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
        // ...
	}
複製程式碼

IOC 容器在例項化我們的 Bean 時會呼叫 ApplicationContextAwareProcessor.postProcessBeforeInitialization 方法,該方法會檢查我們的 Bean,我們的 Bean 如果實現了 ApplicationEventPublisherAware,那麼就會回撥 setApplicationEventPublisher 方法將 applicationContext(即ApplicationEventPublisher) 傳給我們,我們就能夠釋出事件。

BeanFactory.getBean

BeanFactory 的幾個過載了的 getBean 方法是 Bean 最終進行例項化的地方,registerBeanPostProcessorsinvokeBeanFactoryPostProcessors 和 finishBeanFactoryInitialization 方法都呼叫了 getBean 方法對一些特定 Bean 進行了例項化。

finishBeanFactoryInitialization 中通過呼叫 BeanFactory 的 preInstantiateSingletons 對單例 Bean 進行例項化。BeanFactory 和 BeanDefinition 都具有父子的概念,在子級找不到指定的 Bean 時將一直往上(父級)找,找到就進行例項化

總結

spring IOC 容器的啟動步驟可總結如下: 1 初始化 ApplicationContext 環境屬性的初始化和驗證,啟動時間記錄和相關標記設定,應用事件和監聽者的初始化。

2 準備好容器中的 BeanDefinition (eager-initializing beans) 對 BeanDefinition 的解析、掃描和註冊,BeanDefinition 的掃描和註冊大致可以分為 XML 和註解兩種,兩種方式各自使用的元件有所不同,該步驟的時間也可以在最前面。

3 初始化 BeanFactory 準備好 BeanFactory 以供 ApplicationContext 進行使用,對接下來將要使用到的 Bean 進行例項化,資源進行準備,屬性進行設定。

4 註冊 BeanPostProcessors BeanPostProcessors 是進行擴充套件的關鍵元件,需要在該步驟中進行註冊,可分為兩種型別: 一種是框架使用者提供的,用於特定業務功能的,另一種是框架開發者提供的,用於擴充套件框架功能。

5 呼叫 BeanDefinitionRegistryPostProcessor BeanDefinitionRegistryPostProcessor 是一種功能增強,可以在這個步驟新增新的 BeanDefinition 到 BeanFactory 中。

6 呼叫 BeanFactoryPostProcessor BeanFactoryPostProcessor 是一種功能增強,可以在這個步驟對已經完成初始化的 BeanFactory 進行屬性覆蓋,或是修改已經註冊到 BeanFactory 的 BeanDefinition

7 初始化 MessageSource 和 ApplicationEventMulticaster MessageSource 用於處理國際化資源,ApplicationEventMulticaster 是應用事件廣播器,用於分發應用事件給監聽者。

8 初始化其他 Bean 和進行其他的的上下文初始化 主要用於擴充套件

9 註冊 ApplicationListene 將 ApplicationListene 註冊到 BeanFactory 中,以便後續的事件分發

10 例項化剩餘的 Bean 單例 步驟 4 到 9 都對一些特殊的 Bean 進行了例項化,這裡需要對所有剩餘的單例 Bean 進行例項化

11 啟動完成 資源回收,分發"重新整理完成"事件。