理解循環(huán)依賴問題,首先明白spring有四種注入方式滥沫。
第一種,SET注入
a類中持有b類的引用键俱,并且a類有b的set方法兰绣。在bean中添加<property>標(biāo)簽即可注入。實(shí)質(zhì)上是將b實(shí)例化编振,然后調(diào)用set方法注入缀辩。
<bean id="a" class="com.qunar.pojo.StudentA" scope="singleton">
<property name="studentB" ref="b"></property>
</bean>
第二種,構(gòu)造器注入
a類中持有b類的引用踪央,并且a的構(gòu)造函數(shù)參數(shù)中有b臀玄。實(shí)質(zhì)上就是通過構(gòu)造函數(shù)注入,創(chuàng)建a對象時(shí)要把b對象傳進(jìn)去畅蹂。
<bean id="a" class="com.qunar.pojo.StudentA">
<constructor-arg index="0" ref="b"></constructor-arg>
</bean>
第三種健无,靜態(tài)工廠
如果有需要靜態(tài)工廠實(shí)例化的類,不能通過靜態(tài)工廠.方法實(shí)現(xiàn)液斜。在bean屬性中對應(yīng)類指向靜態(tài)工廠累贤,對應(yīng)方法指向返回實(shí)例的方法
第四種,實(shí)例工廠
如果工廠不是靜態(tài)少漆,需要實(shí)例化臼膏,就實(shí)例化對應(yīng)工廠,設(shè)定factory-bean和factory-method進(jìn)行方法調(diào)用示损。
設(shè)定三個(gè)實(shí)體類渗磅,StudentA,B,C代碼如下,A持有B,B持有C,C持有A
public class StudentA {
private StudentB studentB ;
public void setStudentB(StudentB studentB) {
this.studentB = studentB;
}
public StudentA() {
}
public StudentA(StudentB studentB) {
this.studentB = studentB;
}
}
當(dāng)我通過構(gòu)造器注入時(shí),會產(chǎn)生BeanCurrentlyInCreationException異常始鱼。為什么會出現(xiàn)這種異常论巍,spring如何加載實(shí)體?
Spring容器會將每一個(gè)正在創(chuàng)建的Bean 標(biāo)識符放在一個(gè)“當(dāng)前創(chuàng)建Bean池”中风响,Bean標(biāo)識符在創(chuàng)建過程中將一直保持在這個(gè)池中,因此如果在創(chuàng)建Bean過程中發(fā)現(xiàn)自己已經(jīng)在“當(dāng)前創(chuàng)建Bean池”里時(shí)將拋出BeanCurrentlyInCreationException異常表示循環(huán)依賴丹禀;而對于創(chuàng)建完畢的Bean將從“當(dāng)前創(chuàng)建Bean池”中清除掉状勤。
Spring容器先創(chuàng)建單例StudentA,StudentA依賴StudentB双泪,然后將A放在“當(dāng)前創(chuàng)建Bean池”中持搜,此時(shí)創(chuàng)建StudentB,StudentB依賴StudentC ,然后將B放在“當(dāng)前創(chuàng)建Bean池”中,此時(shí)創(chuàng)建StudentC,StudentC又依賴StudentA焙矛, 但是葫盼,此時(shí)Student已經(jīng)在池中,所以會報(bào)錯(cuò)村斟,因?yàn)樵诔刂械腂ean都是未初始化完的贫导,所以會依賴錯(cuò)誤.
解決這個(gè)問題,可以用setter注入的方式蟆盹。
Spring是先將Bean對象實(shí)例化之后孩灯,再設(shè)置對象屬性。所以會先調(diào)用他的無參構(gòu)造函數(shù)實(shí)例化逾滥。每個(gè)對象存在一個(gè)map中峰档。當(dāng)遇到依賴,就去map中調(diào)用對應(yīng)的單例對象寨昙。
另外: 對于“prototype”作用域Bean讥巡,Spring容器無法完成依賴注入,因?yàn)椤皃rototype”作用域的Bean舔哪,Spring容器不進(jìn)行緩存欢顷,因此無法提前暴露一個(gè)創(chuàng)建中的Bean。
Spring裝配Bean的過程
- 實(shí)例化;
- 設(shè)置屬性值;
- 如果實(shí)現(xiàn)了BeanNameAware接口,調(diào)用setBeanName設(shè)置Bean的ID或者Name;
- 如果實(shí)現(xiàn)BeanFactoryAware接口,調(diào)用setBeanFactory 設(shè)置BeanFactory;
- 如果實(shí)現(xiàn)ApplicationContextAware,調(diào)用setApplicationContext設(shè)置ApplicationContext
- 調(diào)用BeanPostProcessor的預(yù)先初始化方法;
- 調(diào)用InitializingBean的afterPropertiesSet()方法;
- 調(diào)用定制init-method方法尸红;
- 調(diào)用BeanPostProcessor的后初始化方法;
Spring容器關(guān)閉過程
- 調(diào)用DisposableBean的destroy();
-
調(diào)用定制的destroy-method方法;
了解了bean默認(rèn)是單例模式吱涉,不由想spring的單例和設(shè)計(jì)模式單例同一種嗎?其實(shí)不一樣外里。單例模式是指在一個(gè)JVM進(jìn)程中僅有一個(gè)實(shí)例怎爵,而Spring單例是指一個(gè)Spring Bean容器(ApplicationContext)中僅有一個(gè)實(shí)例。如果有多個(gè)Spring容器盅蝗,可能有多個(gè)Bean對象鳖链。
spring單例是一種類似注冊表實(shí)現(xiàn)的方式。利用hashmap,向map中注冊和取值芙委,思路類似下面代碼
public class Singleton {
private static Map<String,Singleton> map = new HashMap<String,Singleton>();
static{
Singleton single = new Singleton();
map.put(single.getClass().getName(), single);
}
//保護(hù)的默認(rèn)構(gòu)造子
protected Singleton(){}
//靜態(tài)工廠方法,返還此類惟一的實(shí)例
public static Singleton getInstance(String name) {
if(name == null) {
name = Singleton.class.getName();
}
if(map.get(name) == null) {
try {
map.put(name, (Singleton) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
}
參考文檔:
http://blessht.iteye.com/blog/1162131
http://blog.csdn.net/u010644448/article/details/59108799
http://jinnianshilongnian.iteye.com/blog/1415278