1. 程式人生 > >Spring 迴圈引用 ——理解singleton與prototype初始化的區別

Spring 迴圈引用 ——理解singleton與prototype初始化的區別

所謂的迴圈引用,就是A依賴B,B又依賴A,A與B兩個物件相互持有。像下面這種情況:

 

class A
{
	 B b;
	public A(B b) {
		this.b=b;
	}
}

class B
{
	A a;
	public B(A a )
	{
		this.a=a;
	}
	
}

我們知道spring在獲取物件或者在載入的時候,觸發依賴注入。例如觸發A物件的依賴注入,發現需要B物件,而此時B還沒有初始化,就去例項化B物件,而又需要A物件,這樣就進入了一種死迴圈狀態,有點像作業系統裡面的死鎖。似乎這種情況發生了,Spring就陷入僵局了。顯然Spring有方法去解決這個問題。對於依賴注入的情況,大致分為構造注入和設值注入兩種方式,而實際上因為Spring注入的觸發機制不一樣,這個問題又被分為singleton物件和prototype(其他三個作用域大致相同)物件的區別。我們可以把問題大致分為三類。

 

1 singleton物件迴圈引用

1.1 構造器迴圈依賴

這種依賴Spring是無法給你解決的,將會丟擲BeanCurrentlyInCreationException異常。來看一下測試程式碼

(1) 定義兩個類 A,B 構成構造成迴圈依賴的情況

 

class A
{
	 B b;
	public A(B b) {
		this.b=b;
	}
}

class B
{
	A a;
	public B(A a )
	{
		this.a=a;
	}
	
}


(2) 配置檔案中配置兩個類,在不指明作用域的時候,預設為singleton物件

 

 

<bean id="xiaoA" class="com.songxu.learn.A">
		<constructor-arg name="b" ref="xiaoB" />
	</bean>

	<bean id="xiaoB" class="com.songxu.learn.B">
		<constructor-arg name="a" ref="xiaoA" />
	</bean>


(3)載入配置檔案,兩個類預設被載入以及注入

 

 

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
<span style="font-size:18px;">		</span>

(4) 結果丟擲了異常,來看一下日誌輸出

 

 

2015-08-01 02:39:15,869>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.s[email protected]2ef5e5e3: defining beans [xiaoA,xiaoB]; root of factory hierarchy
  2015-08-01 02:39:15,869>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoA'
  2015-08-01 02:39:15,869>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoA'
  2015-08-01 02:39:15,889>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoB'
  2015-08-01 02:39:15,889>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoB'
  2015-08-01 02:39:15,889>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoA'
  2015-08-01 02:39:15,889>>>>[org.springframework.context.support.AbstractApplicationContext]-[WARN] >>>>Exception encountered during context initialization - cancelling refresh attempt
  org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xiaoA' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'xiaoB' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xiaoB' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'xiaoA' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'xiaoA': Requested bean is currently in creation: Is there an unresolvable circular reference?


這個過程是這樣的,容器構造Class A的物件,發現構造引數需要一個ClassB的物件,而此時容器中沒有ClassB的例項,因此轉到ClassB的例項化過程,ClassB構造又需要ClassA,這樣進入了迴圈狀態。雙方都無法完成構造的過程,因此丟擲了異常。

 

1.2 設值注入迴圈依賴

對於設值注入的情況,迴圈依賴是可以完成的。來看一下測試程式碼。

(1) 構造兩個類 A,B  構成迴圈依賴,並且使用設值注入的方式。

 

class CircleA
{
	private CircleB b;
	public CircleA(CircleB b)
	{
		this.b=b;
	}
	public CircleB getB() {
		return b;
	}
	public void setB(CircleB b) {
		this.b = b;
	}
	public CircleA()
	{
		
	}

}

class CircleB
{
	private CircleA a;
	
	public CircleA getA() {
		return a;
	}

	public void setA(CircleA a) {
		this.a = a;
	}

	public CircleB(CircleA a)
	{
		this.a=a;
	}
	public CircleB()
	{
		
	}
}


(2) 配置檔案 採用預設的方式

 

 

<bean id="circleA" class="com.songxu.learn.CircleA" >
		<property name="b" ref="circleB"></property>
	</bean>
	<bean id="circleB" class="com.songxu.learn.CircleB" >
		<property name="a" ref="circleA"></property>
	</bean>


(3)載入配置檔案,兩個類預設被載入以及注入

 

 

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");


(4)結果執行通過,看一下日誌輸出

 

 

2015-08-01 03:41:32,302>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.s[email protected]100fc185: defining beans [circleA,circleB]; root of factory hierarchy
  2015-08-01 03:41:32,302>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleA'
  2015-08-01 03:41:32,302>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleA' to allow for resolving potential circular references
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleB'
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleB'
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleB' to allow for resolving potential circular references
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning eagerly cached instance of singleton bean 'circleA' that is not fully initialized yet - a consequence of a circular reference
  2015-08-01 03:41:32,322>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleB'
  2015-08-01 03:41:32,322>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleA'
  2015-08-01 03:41:32,322>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'circleB'

這個過程是這樣的, A構造空物件放入快取池中(暫且稱為A空),觸發了B物件注入,然後去構造B物件,又觸發了對A物件的注入,這個時候快取中已經有一個空的A物件,就把A空物件注入給B, 然後B構造完成,返回給A,這時A物件已經完成了注入。而同時B中持有的A物件也不再是A空了, 因為A是單例的,且A完成了注入,此時A=A空。也就完成了整個注入過程。

 

1.3設值與構造混合的迴圈依賴

依然設定A,B兩個類,構成迴圈依賴,A採用設值注入B,B採用構造器注入A。對於這種情況,Spring是可以完成注入的。來檢視示例程式碼

(1) 構造兩個類 A,B 構成迴圈依賴關係,A採用設值注入B,B採用構造器注入A

 

class A
{
	 B b;
	
	public B getB()
	{
		return b;
	}
	public void setB(B b)
	{
		this.b = b;
	}
}

class B
{
	A a;
	public B(A a )
	{
		this.a=a;
	}
	
}


(2) 配置檔案中配置兩個類,在不指明作用域的時候,預設為singleton物件

<bean id="xiaoA" class="com.songxu.learn.A">
		<constructor-arg name="b" ref="xiaoB" />
	</bean>

	<bean id="xiaoB" class="com.songxu.learn.B">
		<constructor-arg name="a" ref="xiaoA" />
	</bean>


(3)載入配置檔案,兩個類預設被載入以及注入

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
<span style="font-size:18px;">		</span>

(4) 結果正常,A,B注入完成,檢視一下日誌輸出

 

 

2015-08-01 04:00:32,884>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.s[email protected]6d00a15d: defining beans [xiaoA,xiaoB]; root of factory hierarchy
  2015-08-01 04:00:32,884>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoA'
  2015-08-01 04:00:32,884>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoA'
  2015-08-01 04:00:32,894>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'xiaoA' to allow for resolving potential circular references
  2015-08-01 04:00:32,904>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoB'
  2015-08-01 04:00:32,904>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoB'
  2015-08-01 04:00:32,904>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning eagerly cached instance of singleton bean 'xiaoA' that is not fully initialized yet - a consequence of a circular reference
  2015-08-01 04:00:32,914>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'xiaoB' to allow for resolving potential circular references
  2015-08-01 04:00:32,914>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'xiaoB'
  2015-08-01 04:00:32,924>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'xiaoA'
  2015-08-01 04:00:32,924>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'xiaoB'

 

 

1.4 Singleton的迴圈依賴注入總結

兩個類構成迴圈依賴並且需要完成Spring注入的唯一條件是,可以構造一個空的物件放入快取,否則將會失敗。而這種空物件的構造必須採用設值注入的方式。在後面將給出深度解析。

2 Prototype的迴圈依賴

依然採用上面的三種情況去分析

2.1 構造器依賴注入

(1)類與上面的一樣,只是配置檔案稍作修改

 

<bean id="xiaoA" class="com.songxu.learn.A" scope="prototype">
		<constructor-arg name="b" ref="xiaoB" />
		<!-- <property name="b"  ref="xiaoB"></property> -->
	</bean>

	<bean id="xiaoB" class="com.songxu.learn.B" scope="prototype">
		<constructor-arg name="a" ref="xiaoA" />
	</bean>


(2)測試程式碼需要呼叫getBean方法,否則無法觸發依賴注入

 

 

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
		A xiaoA=(A)applicationContext.getBean("xiaoA");


(3)結果丟擲異常BeanCurrentlyInCreationException。下面看日誌輸出

 

 

2015-08-01 04:09:31,425>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoA'
  2015-08-01 04:09:31,435>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoB'
  Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xiaoA' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'xiaoB' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xiaoB' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'xiaoA' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'xiaoA': Requested bean is currently in creation: Is there an unresolvable circular reference?

 

 

2.2 設值注入

 

(1)類與上面的一樣,只是配置檔案稍作修改

<span style="font-size:18px;"><bean id="circleA" class="com.songxu.learn.CircleA"  scope="prototype">
        <property name="b" ref="circleB"></property>
    </bean>
    <bean id="circleB" class="com.songxu.learn.CircleB" scope="prototype" >
        <property name="a" ref="circleA"></property>
    </bean>  

</span>


(2)測試程式碼需要呼叫getBean方法,否則無法觸發依賴注入

<span style="font-size:18px;">ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        CircleB b=(CircleB)applicationContext.getBean("circleB");
        
</span>


(3)結果丟擲異常BeanCurrentlyInCreationException。下面看日誌輸出 

 

2015-08-01 04:13:29,505>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleB'
  2015-08-01 04:13:29,515>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circleB' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'circleA' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circleA' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'circleB' while setting bean property 'b'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circleB': Requested bean is currently in creation: Is there an unresolvable circular reference?

 

 

2.3 混合模式

測試程式碼與上面類似,結果同樣是無法完成注入,丟擲BeanCurrentlyInCreationException異常。

2.4 小結

對於prototype來說,只有存在迴圈引用的情況,是無法完成注入的。

 

3 prototype與singleton的混合模式迴圈引用

這種混合模式在某些情況下是成立的。這裡只給出一種成功的案例,其他模式下無法完成。這一成功的型別是AB兩個類都採用設值注入的方式。

(1) 構造A ,B兩個類,構成迴圈依賴關係

 

class CircleA
{
	private CircleB b;
	
	public CircleB getB() {
		return b;
	}
	public void setB(CircleB b) {
		this.b = b;
	}
	public CircleA()
	{
		
	}

}

class CircleB
{
	private CircleA a;
	public CircleB()
	{
		
	}
	public CircleA getA() {
		return a;
	}

	public void setA(CircleA a) {
		this.a = a;
	}

	}


(2) 配置檔案 配置A為prototype B為預設  AB均採用設值注入的方式

 

 

<bean id="circleA" class="com.songxu.learn.CircleA" scope="prototype" >
		<property name="b" ref="circleB"></property>  
		
	</bean>
	<bean id="circleB" class="com.songxu.learn.CircleB" >
		<property name="a" ref="circleA"></property>
		
	</bean>


(3) 測試結果 通過,而且B中持有的A物件 與getBean得到的A物件不是一個物件。

 

 

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
		CircleA A=(CircleA)applicationContext.getBean("circleA");
		CircleB B=(CircleB)applicationContext.getBean("circleB");
		System.out.println(A==B.getA());


(4)檢視一下日誌輸出

 

 

2015-08-01 04:45:08,952>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.s[email protected]100fc185: defining beans [circleA,circleB]; root of factory hierarchy
  2015-08-01 04:45:08,952>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleB'
  2015-08-01 04:45:08,952>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleB'
  2015-08-01 04:45:08,962>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleB' to allow for resolving potential circular references
  2015-08-01 04:45:08,962>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  2015-08-01 04:45:08,962>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning eagerly cached instance of singleton bean 'circleB' that is not fully initialized yet - a consequence of a circular reference
  2015-08-01 04:45:08,972>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleA'
  2015-08-01 04:45:08,972>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleB'
  2015-08-01 04:45:08,972>>>>[org.springframework.context.support.AbstractApplicationContext]-[DEBUG] >>>>Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [[email protected]fe2924]
  2015-08-01 04:45:08,972>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'lifecycleProcessor'
  2015-08-01 04:45:08,982>>>>[org.springframework.core.env.PropertySourcesPropertyResolver]-[DEBUG] >>>>Searching for key 'spring.liveBeansView.mbeanDomain' in [systemProperties]
  2015-08-01 04:45:08,982>>>>[org.springframework.core.env.PropertySourcesPropertyResolver]-[DEBUG] >>>>Searching for key 'spring.liveBeansView.mbeanDomain' in [systemEnvironment]
  2015-08-01 04:45:08,982>>>>[org.springframework.core.env.PropertySourcesPropertyResolver]-[DEBUG] >>>>Could not find key 'spring.liveBeansView.mbeanDomain' in any property source. Returning [null]
  init finished============================================
2015-08-01 04:45:08,982>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  2015-08-01 04:45:08,982>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'circleB'
  2015-08-01 04:45:08,982>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleA'
  2015-08-01 04:45:08,982>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'circleB'
  false


 

 

4 singleton與prototype的初始化的區別

4.1 singleton初始化過程

下面的示意圖大致顯示了這個過程的關鍵步驟,關於細節的程式碼,可以檢視另一篇部落格。

這個圖中的數字標識了各個過程,紅色填圖的區域表示關鍵步驟,黃色填圖的區域為logger日誌輸出內容,虛線邊框代表一個方法的內部。

下面是一段 singleon物件迴圈引用的日誌輸出。用來配合理解這個過程

 

2015-08-01 09:51:34,966>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.s[email protected]100fc185: defining beans [circleA,circleB]; root of factory hierarchy
  2015-08-01 09:51:34,966>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleA'
  2015-08-01 09:51:34,966>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  A開始初始化
2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleA' to allow for resolving potential circular references
  2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleB'
  2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleB'
  B開始初始化
2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleB' to allow for resolving potential circular references
  2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning eagerly cached instance of singleton bean 'circleA' that is not fully initialized yet - a consequence of a circular reference
  B開始注入
B完成注入
2015-08-01 09:51:34,986>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleB'
  A開始注入
A完成注入
2015-08-01 09:51:34,986>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleA'

 

 

4.1.1 輔助關鍵屬性

在DefaultSingletonBeanRegistry類中,定義了一些屬性,它決定著整個依賴注入過程的關鍵步驟跳轉,同時也伴隨著整個bean的關鍵週期.

 

/** 儲存所有的singletonBean的例項 */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

	/** singletonBean的生產工廠*/
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

	/** 儲存所有早期建立的Bean物件,這個Bean還沒有完成依賴注入 */
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

	/** 儲存所有已經完成初始化的Bean的名字(name) */
	private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);

	/** 標識指定name的Bean物件是否處於建立狀態  這個狀態非常重要 */
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

 

4.1.2 singleton的建立流程

singleton的建立開始於getBean方法,關於這個流程,可以檢視另外一篇部落格。這個方法呼叫了doGetBean方法,也是示意圖中最頂端的方法,這是建立singleton的開始。

  1. 進入1號程式碼區域,這是一個關鍵的程式碼,getSingleton方法去查詢快取,如果存在未建立完成的Bean(earlyBean),就返回這個結果,作為doGetBean的返回值,並輸出一段日誌內容,流程結束:
  2. 否則進入2號程式碼區,建立一個ObjectFactory,作為建立singleton的工廠,註冊給getSingleton方法,進入紅色方法區;
  3. 輸出3號日誌內容;
  4. 進入4號方法,這是一個關鍵節點,在這裡把當前正常初始化的BeanName放入singletonsCurrentlyInCreation集合中,此時與BeanName繫結的屬性為true,表示這個beanName正在建立;
  5. 進入5號方法,也就是綠色程式碼區,這是呼叫前面註冊的工廠的建立方法,也是Spring中所有的Bean的初始化的入口;
  6. 輸出6號日誌內容;
  7. 呼叫doGetBean方法,進入藍色程式碼區,這是建立Bean的核心程式碼區;
  8. 進入7號關鍵程式碼區,這個地方完成了構造方法的匹配工作,構造注入與設值注入的區別點就在這;採用構造注入的話,需要在這裡面獲得所有構造方法引數的屬性值,如果屬性值為另一個Bean的話,就會呼叫getBean方法,遞迴地回到整個流程的入口處;注意,在這個時候4.1.1中的關鍵屬性的狀態為 singletonsCurrentlyInCreation.get(Beanname)=true;singgletonFactories.get(name)=null;singletonObjects.get(beanName)=null;
  9. 如果採用設值注入的話,此處僅僅會呼叫預設的空的構造方法,此時已經建立了一個空的Bean物件(earlyBean)然後向下進入8號區域;
  10. 進入8號區域輸出日誌內容;
  11. 進入9號方法,這是一個singleton建立後的一個後處理方法,在這裡又一次註冊了一個工廠,並且註冊的工廠方法為getEarlyBeanReference,並把這個方法加入到singletonFactories中,此時singletonFactories.get(name)!=null;
  12. 進入10號程式碼區,這裡完成了依賴注入的過程,如果需要注入Bean的話,就會呼叫getBean的方法;
  13. 進入11號區域,整個singletonBean建立完成,輸出日誌內容,然後進行一些後處理工作,流程退出

4.1.3原始碼

整個過程的原始碼很多,這裡僅擷取與singleton建立相關的部分

(1)圖中1號程式碼getSingleton

 

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//在已經完成建立的singleton集合中查詢
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果沒有找到 並且還處於當前建立狀態,就應該可以找到earlyReference
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);//在earlyReference查詢
				if (singletonObject == null && allowEarlyReference) {
					//如果還沒有找到,就去找對應的工廠方法,這裡與9號程式碼迴應。
					//如果建立了空的Bean的話,在此處一定能拿到對應的工廠
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();//在這獲得對應的earlyBean
						this.earlySingletonObjects.put(beanName, singletonObject);//放入earlyBeanObjects集合中
						this.singletonFactories.remove(beanName);//移除工廠方法
					}
				}
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}


(2)第一個日誌輸出的地方,這裡的getSingleton就是上面(1)裡面的方法

 

 

// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}


(3) 如果getSingleton返回null,就進入建立階段

 

 

if (mbd.isSingleton()) {
					//註冊工廠  呼叫另一個getSingleton
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							try {
								//建立Bean的實際方法
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								// Explicitly remove instance from singleton cache: It might have been put there
								// eagerly by the creation process, to allow for circular reference resolution.
								// Also remove any beans that received a temporary reference to the bean.
								destroySingleton(beanName);
								throw ex;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}


(4) 紅色程式碼框的getSingleton的方法

 

 

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
			//非空校驗
		Assert.notNull(beanName, "'beanName' must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while the singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				//前置處理
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<Exception>();
				}
				try {
					//呼叫工程方法
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					//後置處理
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return (singletonObject != NULL_OBJECT ? singletonObject : null);
		}
	}


(5) beforeSingletonCreation 前置處理, 在這可能丟擲重複建立的異常

 

 

protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}


(6)doCreateBean方法 已略去無關程式碼

 

 

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		//...
		if (instanceWrapper == null) {
			//建立Bean的實際方法
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		//...

		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");
			}
			//註冊getEaryBean的工廠方法
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//開始依賴注入  觸發getBean方法
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				exposedObject = initializeBean(beanName, exposedObject, mbd);
			}
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		//...

		return exposedObject;
	}

 

 

(7 )createBeanInstance方法,實際建立Bean的方法

 

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
		//...

		// 在這裡去匹配合適的構造方法,如果存在構造注入,需要找到所有的注入的值,觸發getBean
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);
	}

 

 

4.1.4不能採用構造注入的原因

通過上面的分析,已經基本可以看出設值注入與構造注入的區別在哪裡,就是觸發getBean遞迴的時機不一致,導致1號的getSingleton的結果不一樣。

對於設值注入來說,首先進入getA的getBean,此時getSingleton返回null,進入Bean的建立階段,此時採用設值注入,構造預設的空的Bean,並註冊一個工廠方法,然後依賴注入,getBean的時候觸發了對B的構造過程,與A一樣,構造預設的空的Bean,並註冊一個工廠方法,然後依賴注入,這時需要A,第二次進入A的getBean流程,而此時因為存在工廠方法,所以1號的getSingleton不再返回空,這樣就返回了一個Earlybean物件注入到B中;然後B完成注入,接著A完成注入。

對於構造注入來說,首先進入getA的getBean,此時getSingleton返回null,進入Bean的建立階段,此時採用構造注入,在查詢注入引數的時候,就觸發了B的getBean方法,注意此時並沒有註冊工廠方法,B的建立流程開始,採用構造注入,查詢注入引數,第二次進入A的getBean方法,因為沒有提供可以拿到earlyBean的工廠方法,1號getSingleton將返回空,此時將再次進入紅色方法區,再次呼叫4號方法時,就會丟擲重複建立的異常。

對於混合方式來說,因為其中之一採用設值注入,再第二個進入這個物件的getBean方法的時候,總會得到earlyBean,因此不會丟擲異常,也就可以完成建立。

在此可以得出結論,Spring對於迴圈引用的解決方案就是暴露一個ObjectFactroy,對於未建立完成的Bean,可以通過這個工廠得到一個earlyBean保證“鎖”被釋放,完成流程。而可以暴露這個ObjectFactory的唯一條件就是必須採用設值注入,以保證可以提前構造一個空的earlyBean。

4.2 prototype的初始化流程

4.2.1 prototype的初始化流程

相對於singleton來說,prototype就簡單的多,由於方法名都類似,在這直接給出整個流程的程式碼。

(1) 關鍵屬性  只有一個,儲存著正在建立的prototype的beanName

 

/** Names of beans that are currently in creation */
	private final ThreadLocal<Object> prototypesCurrentlyInCreation =
			new NamedThreadLocal<Object>("Prototype beans currently in creation");


(2) 流程程式碼 ,這裡沒有像前面給出那麼多程式碼的原因,是因為後面的程式碼都一樣,並沒有新增與prototype相關的,也就是說prototype的建立流程也米有對外暴露什麼工廠之類的輔助。 

 

 

//流程1  檢驗當前的bean是否處於
	// Fail if we're already creating this bean instance:
	// We're assumably within a circular reference.
	if (isPrototypeCurrentlyInCreation(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
	//流程2  建立Bean
	else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
		try {
			    //前置處理
				beforePrototypeCreation(beanName);
				//建立流程的開始
				prototypeInstance = createBean(beanName, mbd, args);
			}
		finally {
				afterPrototypeCreation(beanName);
			}
		bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
	}


(3) 前置處理、檢驗部分程式碼

 

前置處理看著挺複雜,其實就是把每個正在建立的prototype的BeanName放入一個set中

 

protected void beforePrototypeCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		if (curVal == null) {
			this.prototypesCurrentlyInCreation.set(beanName);
		}
		else if (curVal instanceof String) {
			Set<String> beanNameSet = new HashSet<String>(2);
			beanNameSet.add((String) curVal);
			beanNameSet.add(beanName);
			this.prototypesCurrentlyInCreation.set(beanNameSet);
		}
		else {
			Set<String> beanNameSet = (Set<String>) curVal;
			beanNameSet.add(beanName);
		}
	}


檢驗就是檢查當前的beanName是否處於建立狀態,如果是就丟擲異常 

 

 

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


 

 

4.2.2 prototype不能實現迴圈引用的原因

從流程上就可以檢視,無論是構造注入還是設值注入,第二次進入同一個Bean的getBean方法是,一定會在校驗部分丟擲異常,因此不能完成注入,也就不能實現迴圈引用。