我的架構夢:(六)Spring框架中的IoC容器源碼分析(下)

上一篇:我的架構夢:(五)Spring框架中的IoC容器源碼分析(上)

四、BeanFactory創建流程

1、獲取BeanFactory子流程

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	...
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		/**
		 * 對象鎖加鎖
		 */
		synchronized (this.startupShutdownMonitor) {
			...
			// 我這裏直接定位到該流程需要的代碼,其他的無關代碼省略了哈
			/**
			 * 第二步:
			 *
			 * 獲取BeanFactory:默認實現是DefaultListableBeanFactory
			 * Bean獲取並封裝成BeanDefinition對象
			 * 加載BeanDefinition 並註冊到BeanDefinitionRegistry
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			...
		}
	}
	...
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	refreshBeanFactory();
	return getBeanFactory();
}
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
	...
	/**
	 * 對BeanFactory執行刷新操作,關閉以前的BeanFactory(如果有)
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		// 判斷是否已有BeanFactory
		if (hasBeanFactory()) {
			// 銷燬Beans
			destroyBeans();
			// 關閉BeanFactory
			closeBeanFactory();
		}
		try {
			// 實例化DefaultListableBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			// 設置序列化id
			beanFactory.setSerializationId(getId());
			// 自定義bean工廠的一些屬性(是否覆蓋、是否允許循環依賴)
			customizeBeanFactory(beanFactory);
			// 加載應用中的BeanDefinitions
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				// 賦值當前BeanFactory
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
	...
}

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
	...
	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		synchronized (this.beanFactoryMonitor) {
			if (this.beanFactory == null) {
				throw new IllegalStateException("BeanFactory not initialized or already closed - " +
						"call 'refresh' before accessing beans via the ApplicationContext");
			}
			return this.beanFactory;
		}
	}
	...
}

2、BeanDefinition加載解析及註冊子流程

2.1 該子流程涉及到如下幾個關鍵步驟

Resource定位:指對BeanDefinition的資源定位過程。通俗講就是找到定義Javabean信息的XML文 件,並將其封裝成Resource對象。

BeanDefinition載入 :把用戶定義好的Javabean表示爲IoC容器內部的數據結構,這個容器內部的數據結構就是BeanDefinition

2.2 源碼過程分析

Step 1:子流程入口在我們寫的測試類的ClassPathXmlApplicationContext構造方法中

在這裏插入圖片描述

Step 2:繼續跟進 ClassPathXmlApplicationContext#ClassPathXmlApplicationContext 方法中

在這裏插入圖片描述

Step 3:繼續跟進 AbstractRefreshableApplicationContext#refreshBeanFactory 方法中

在這裏插入圖片描述
Step 4:繼續跟進 AbstractApplicationContext#obtainFreshBeanFactory#refreshBeanFactory 方法中

在這裏插入圖片描述
在這裏插入圖片描述
Step 5:依次調用多個類的 loadBeanDefinitions 方法 ​ —> AbstractXmlApplicationContext ​ —> AbstractBeanDefinitionReader —> XmlBeanDefinitionReader ​ 一直執行到 XmlBeanDefinitionReaderdoLoadBeanDefinitions 方法

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

Step 6:我們重點觀察XmlBeanDefinitionReader 類的 registerBeanDefinitions 方法,期間產生了多 次重載調用,我們定位到最後一個。

在這裏插入圖片描述
此處我們關注兩個地方:一個createRederContext方法,一個是 DefaultBeanDefinitionDocumentReader類的registerBeanDefinitions方法,先進入 createRederContext 方法看看。

在這裏插入圖片描述

我們可以看到,此處 Spring 首先完成了 NamespaceHandlerResolver 的初始化。

我們再進入 registerBeanDefinitions 方法中追蹤,調用了
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions 方法。

在這裏插入圖片描述

在這裏插入圖片描述
進入 parseBeanDefinitions 方法:

在這裏插入圖片描述

進入 parseDefaultElement 方法:
在這裏插入圖片描述

進入 processBeanDefinition 方法:

在這裏插入圖片描述
至此,註冊流程結束,我們發現,所謂的註冊就是把封裝的 XML 中定義的 Bean信息封裝爲 BeanDefinition 對象之後放入一個Map中,BeanFactory 是以 Map 的結構組織這些 BeanDefinition 的。

Step 6:繼續跟進BeanDefinitionReaderUtils#registerBeanDefinition方法

在這裏插入圖片描述
Step 7:繼續跟進DefaultListableBeanFactory#registerBeanDefinition方法
在這裏插入圖片描述
可以在DefaultListableBeanFactory中看到此Map的定義:

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

五、Bean創建流程

1、通過最開始的關鍵時機點分析,我們知道Bean創建子流程入口在
AbstractApplicationContext#refresh()方法的finishBeanFactoryInitialization(beanFactory)

在這裏插入圖片描述
2、進入finishBeanFactoryInitialization方法
在這裏插入圖片描述
3、繼續進入DefaultListableBeanFactory類的preInstantiateSingletons方法,我們找到下面部分的 代碼,看到工廠Bean或者普通Bean,最終都是通過getBean的方法獲取實例。

在這裏插入圖片描述

4、繼續跟蹤下去,我們進入到了AbstractBeanFactory類的doGetBean方法,這個方法中的代碼很多,我們直接找到核心部分。

在這裏插入圖片描述

5、接着進入到AbstractAutowireCapableBeanFactory類的方法,找到以下代碼部分。

在這裏插入圖片描述

進入doCreateBean方法看看,該方法我們關注兩塊重點區域:

  • 創建Bean實例,此時尚未設置屬性。

    if (instanceWrapper == null) {
    	// 創建Bean實例,僅僅調用構造方法,但是尚未設置屬性
    	instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    
  • 給Bean填充屬性,調用初始化方法,應用BeanPostProcessor後置處理器。

    try {
    	// Bean屬性填充
    	populateBean(beanName, mbd, instanceWrapper);
    	// 調用初始化方法,應用BeanPostProcessor後置處理器
    	exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    

這樣我們就完成了Bean創建流程的源碼分析。

六、lazy-init 延遲加載機制原理

普通 Bean 的初始化是在容器啓動初始化階段執行的,而被lazy-init=true修飾的 bean 則是在從容器裏第一次進行context.getBean() 時進行觸發。Spring 啓動的時候會把所有bean信息(包括XML和註解)解析轉化成Spring能夠識別的BeanDefinition並存到Hashmap裏供下面的初始化時用,然後對每個 BeanDefinition 進行處理,如果是懶加載的則在容器初始化階段不處理,其他的則在容器初始化階段進 行初始化並依賴注入。

1、入口AbstractApplicationContext#finishBeanFactoryInitialization

/**
 * Finish the initialization of this context's bean factory,
 * initializing all remaining singleton beans.
 *
 * 結束BeanFactory的初始化工作
 * 實例化所有單例bean
 */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	// Initialize conversion service for this context.
	if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
			beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
		beanFactory.setConversionService(
				beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
	}

	// Register a default embedded value resolver if no bean post-processor
	// (such as a PropertyPlaceholderConfigurer bean) registered any before:
	// at this point, primarily for resolution in annotation attribute values.
	if (!beanFactory.hasEmbeddedValueResolver()) {
		beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
	}

	// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
	for (String weaverAwareName : weaverAwareNames) {
		getBean(weaverAwareName);
	}

	// Stop using the temporary ClassLoader for type matching.
	beanFactory.setTempClassLoader(null);

	// Allow for caching all bean definition metadata, not expecting further changes.
	// 實例化所有立即加載的單例bean
	beanFactory.freezeConfiguration();

	// Instantiate all remaining (non-lazy-init) singletons.
	// 實例化所有立即加載的單例bean
	beanFactory.preInstantiateSingletons();
}

2、跟進DefaultListableBeanFactory#preInstantiateSingletons

@Override
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.
	// 所有bean的名字
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
	
	// Trigger initialization of all non-lazy singleton beans...
	// 觸發所有非延遲加載單例bean的初始化,主要步驟爲getBean
	for (String beanName : beanNames) {
		// 合併父BeanDefinition對象
		// map.get(beanName)
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				// 如果是FactoryBean則加&
				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 {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
			}
			else {
				// 實例化當前bean
				getBean(beanName);
			}
		}
	}
	
	// Trigger post-initialization callback for all applicable beans...
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		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();
			}
		}
	}
}

3、小結

  • 對於被修飾爲lazy-initbeanSpring 容器初始化階段不會進行 init 並且依賴注入,當第一次進行getBean時候才進行初始化並依賴注入。
  • 對於非懶加載的beangetBean的時候會從緩存裏頭獲取,因爲容器初始化階段 Bean 已經初始化完成並緩存了起來。

七、Spring IoC 循環依賴問題

1、什麼是循環依賴

循環依賴其實就是循環引用,也就是兩個或者兩個以上的 Bean 互相持有對方,最終形成閉環。比如A 依賴於B,B依賴於C,C又依賴於A。

在這裏插入圖片描述
注意,這裏不是函數的循環調用,是對象的相互依賴關係。循環調用其實就是一個死循環,除非有終結 條件。

Spring中循環依賴場景有:

  • 構造器的循環依賴(構造器注入)
  • Field 屬性的循環依賴(set注入)

其中,構造器的循環依賴問題無法解決,只能拋出 BeanCurrentlyInCreationException 異常,在解決
屬性循環依賴時,spring採用的是提前暴露對象的方法。

2、循環依賴處理機制

  • 單例 bean 構造器參數循環依賴(無法解決)

  • prototype 原型 bean循環依賴(無法解決)
    對於原型bean的初始化過程中不論是通過構造器參數循環依賴還是通過setXxx方法產生循環依 賴,Spring都 會直接報錯處理。
    AbstractBeanFactory.doGetBean()方法:

    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }
    
    protected boolean isPrototypeCurrentlyInCreation(String beanName) { 
    	Object curVal = this.prototypesCurrentlyInCreation.get(); 
    	return (curVal != null && 
    		(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    }
    

    在獲取bean之前如果這個原型bean正在被創建則直接拋出異常。原型bean在創建之前會進行標記 這個beanName正在被創建,等創建結束之後會刪除標記。

    try {
    	//創建原型bean之前添加標記 beforePrototypeCreation(beanName);
    	//創建原型bean
    	prototypeInstance = createBean(beanName, mbd, args);
    } finally {
    	//創建原型bean之後刪除標記
        afterPrototypeCreation(beanName);
    }
    

    總結:Spring 不支持原型 bean 的循環依賴

  • 單例bean通過setXxx或者@Autowired進行循環依賴

    Spring 的循環依賴的理論依據基於 Java 的引用傳遞,當獲得對象的引用時,對象的屬性是可以延 後設置的,但是構造器必須是在獲取引用之前。

    Spring通過setXxx或者@Autowired方法解決循環依賴其實是通過提前暴露一個ObjectFactory對象來完成的,簡單來說ClassA在調用構造器完成對象初始化之後,在調用ClassAsetClassB方法 之前就把ClassA實例化的對象通過ObjectFactory提前暴露到Spring容器中。

    Spring容器初始化ClassA通過構造器初始化對象後提前暴露到Spring容器。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
	if (logger.isDebugEnabled()) {
		logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
	}
	//將初始化後的對象提前已ObjectFactory對象注入到容器中 
	addSingletonFactory(beanName, new ObjectFactory<Object>() {
		@Override
		public Object getObject() throws BeansException {
		    return getEarlyBeanReference(beanName, mbd, bean);
		} 
	});
}

3、循環依賴整體流程

  • ClassA調用setClassB方法,Spring首先嚐試從容器中獲取ClassB,此時ClassB不存在Spring 容器中。
  • Spring容器初始化ClassB,同時也會將ClassB提前暴露到Spring容器中。
  • ClassB調用setClassA方法,Spring從容器中獲取ClassA ,因爲第一步中已經提前暴露了。
    ClassA,因此可以獲取到ClassA實例。
    ClassA通過spring容器獲取到ClassB,完成了對象初始化操作。
  • 這樣ClassAClassB都完成了對象初始化操作,解決了循環依賴問題。

Spring框架中的IoC容器源碼分析到此總算分析完了,如果覺得對你有幫助的話點個贊再走吧,對的起博主奮戰到凌晨兩點寫完的。。。

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