所謂的循環引用,就是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.springframework.beans.factory.support.DefaultListableBeanFactory@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.springframework.beans.factory.support.DefaultListableBeanFactory@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.springframework.beans.factory.support.DefaultListableBeanFactory@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.springframework.beans.factory.support.DefaultListableBeanFactory@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 [org.springframework.context.support.DefaultLifecycleProcessor@1dfe2924]
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.springframework.beans.factory.support.DefaultListableBeanFactory@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號代碼區域,這是一個關鍵的代碼,getSingleton方法去查找緩存,如果存在未創建完成的Bean(earlyBean),就返回這個結果,作爲doGetBean的返回值,並輸出一段日誌內容,流程結束:
- 否則進入2號代碼區,創建一個ObjectFactory,作爲創建singleton的工廠,註冊給getSingleton方法,進入紅色方法區;
- 輸出3號日誌內容;
- 進入4號方法,這是一個關鍵節點,在這裏把當前正常初始化的BeanName放入singletonsCurrentlyInCreation集合中,此時與BeanName綁定的屬性爲true,表示這個beanName正在創建;
- 進入5號方法,也就是綠色代碼區,這是調用前面註冊的工廠的創建方法,也是Spring中所有的Bean的初始化的入口;
- 輸出6號日誌內容;
- 調用doGetBean方法,進入藍色代碼區,這是創建Bean的核心代碼區;
- 進入7號關鍵代碼區,這個地方完成了構造方法的匹配工作,構造注入與設值注入的區別點就在這;採用構造注入的話,需要在這裏面獲得所有構造方法參數的屬性值,如果屬性值爲另一個Bean的話,就會調用getBean方法,遞歸地回到整個流程的入口處;注意,在這個時候4.1.1中的關鍵屬性的狀態爲 singletonsCurrentlyInCreation.get(Beanname)=true; singgletonFactories.get(name)=null;singletonObjects.get(beanName)=null;
- 如果採用設值注入的話,此處僅僅會調用默認的空的構造方法,此時已經創建了一個空的Bean對象(earlyBean)然後向下進入8號區域;
- 進入8號區域輸出日誌內容;
- 進入9號方法,這是一個singleton創建後的一個後處理方法,在這裏又一次註冊了一個工廠,並且註冊的工廠方法爲getEarlyBeanReference,並把這個方法加入到singletonFactories中,此時singletonFactories.get(name)!=null;
- 進入10號代碼區,這裏完成了依賴注入的過程,如果需要注入Bean的話,就會調用getBean的方法;
- 進入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方法是,一定會在校驗部分拋出異常,因此不能完成注入,也就不能實現循環引用。