Spring Ioc原始碼分析系列--Bean範例化過程(一)

2022-05-26 21:01:10

Spring Ioc原始碼分析系列--Bean範例化過程(一)

前言

上一篇文章Spring Ioc原始碼分析系列--Ioc容器註冊BeanPostProcessor後置處理器以及事件訊息處理已經完成了對IoC容器啟動方法也就是refresh()方法的簡單分析。但是之前的分析在對容器範例化Bean的過程的略過了,留到了這後續的文章分析,所以這篇文章會對Bean的範例化過程做一個介紹。

首先來理一下本文的思路:關鍵詞是範例化。由於Spring是利用反射實現的範例化,腦子裡先簡單想一下Java裡利用發射範例化一個物件需要哪些步驟和操作。毫無疑問,我們首先要知道物件的class,接著需要確定使用什麼建構函式以及確定建構函式的引數等。利用這些已經基本可以實現一個物件的範例化,當然實際上需要的東西可能更多更復雜,這裡只是舉個例子。那麼需要的這些資訊可以去哪裡提取呢?對Spring有了解的可能都馬上能想到BeanDefinition,這是一份原料表,裡面有我們構造一個範例化物件所需的所有引數。如果不太理解這個定義,可以參考一下上篇文章的例子。

如果不清楚BeanDefinition是從哪裡來的以及不清楚如何定義的,可以參考之前的文章Spring Ioc原始碼分析系列--Ioc原始碼入口分析的關鍵實現系列方法 loadBeanDefinitions ()。這篇文章講解註冊的時候只是說了註冊到容器裡,並沒有說明具體是註冊到了哪裡,這裡點明一下,所謂講BeanDefinition註冊到容器裡,就是將BeanDefinition放入到容器的一個Map裡,具體是註冊到了DefaultListableBeanFactorybeanDefinitionMap屬性裡,beanName會儲存到beanDefinitionNames屬性裡,這是個list集合,裡面的beanName會保持註冊時候的順序。

範例化的開始就是從遍歷所有的beanName開始,話不多說,開始分析吧。

原始碼分析

bean範例化入口

還記得範例化入口的方法名嗎?回憶一下,算了,反正也不會有人記得。是beanFactory.preInstantiateSingletons(),具體實現是在DefaultListableBeanFactory類裡。

跟程序式碼檢視,可以看到,這段程式碼分為兩部分,第一個for迴圈用於先範例化物件,第二個for迴圈完成一些範例化之後的回撥操作。我們先來看第一個for迴圈,首先是遍歷所有的beanNames獲取BeanDefinition,然後根據工廠bean非工廠bean進行相應處理,最後呼叫getBean(beanName)範例化物件。注意這裡範例化的是非抽象的、單例的並且是非懶載入的bean,這個前提非常重要。

	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		// 所有bd的名稱
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		// 遍歷所有bd,一個個進行建立
		for (String beanName : beanNames) {
			// 獲取到指定名稱對應的bd
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			// 對不是延遲載入的單例的Bean進行建立
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				// 判斷是否是一個FactoryBean
				if (isFactoryBean(beanName)) {
					// 如果是一個factoryBean的話,先建立這個factoryBean,建立factoryBean時,需要在beanName前面拼接一個&符號
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							// 判斷是否是一個SmartFactoryBean,並且不是懶載入的,就意味著,在建立了這個factoryBean之後要立馬呼叫它的getObject方法建立另外一個Bean
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					// 不是factoryBean的話,我們直接建立就行了
					getBean(beanName);
				}
			}
		}

		// Trigger post-initialization callback for all applicable beans...
		// 在建立了所有的Bean之後,遍歷為所有適用的 bean 觸發初始化後回撥,也就是這裡會對延遲初始化的bean進行載入...
		for (String beanName : beanNames) {
			// 這一步其實是從快取中獲取對應的建立的Bean,這裡獲取到的必定是單例的
			Object singletonInstance = getSingleton(beanName);
			// 判斷是否是一個SmartInitializingSingleton,
			// 最典型的就是我們之前分析過的EventListenerMethodProcessor,
			// 在這一步完成了對已經建立好的Bean的解析,會判斷其方法上是否有 @EventListener註解,
			// 會將這個註解標註的方法通過EventListenerFactory轉換成一個事件監聽器並新增到監聽器的集合中
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

獲取BeanDefinition

首先跟進getMergedLocalBeanDefinition(beanName)方法,這裡首先會嘗試從mergedBeanDefinitions裡去獲取,這個mergedBeanDefinitions存放著已經合併過的BeanDefinition,獲取不到再真正呼叫getMergedBeanDefinition(beanName, getBeanDefinition(beanName))去獲取。

	/**
	 * Return a merged RootBeanDefinition, traversing the parent bean definition
	 * if the specified bean corresponds to a child bean definition.
	 *
	 * 返回一個合併的 RootBeanDefinition,如果指定的 bean 對應於子 bean 定義,則遍歷父 bean 定義。
	 *
	 * @param beanName the name of the bean to retrieve the merged definition for
	 * @return a (potentially merged) RootBeanDefinition for the given bean
	 * @throws NoSuchBeanDefinitionException if there is no bean with the given name
	 * @throws BeanDefinitionStoreException in case of an invalid bean definition
	 */
	protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		// Quick check on the concurrent map first, with minimal locking.
		// 首先檢查 mergedBeanDefinitions ,最小程度影響並行效能
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
		if (mbd != null && !mbd.stale) {
			return mbd;
		}
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}

先看getBeanDefinition(beanName),這個方法就是簡單的去beanDefinitionMap裡獲取BeanDefinition,如果獲取不到,就丟擲異常。beanDefinitionMap就是上面說到的BeanDefinition存放的地方。

	public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
		BeanDefinition bd = this.beanDefinitionMap.get(beanName);
		if (bd == null) {
			if (logger.isTraceEnabled()) {
				logger.trace("No bean named '" + beanName + "' found in " + this);
			}
			throw new NoSuchBeanDefinitionException(beanName);
		}
		return bd;
	}

接下來就進入到getMergedBeanDefinition()方法獲取BeanDefinition,為啥要從beanDefinitionMap獲取了還進行一個merged獲取呢?這是因為Bean有層次關係,子類需要合併父類別的屬性方法等,所以要進行一次合併,合併完成後會放入到mergedBeanDefinitions裡,功能和屬性名區分度還是十分貼切的