Spring Bean註入/單例理解/循環依賴
第一種,SET註入
a類中持有b類的引用,並且a類有b的set方法。在bean中添加<property>標簽即可註入。實質上是將b實例化,然後調用set方法註入。
<bean id="a" class="com.qunar.pojo.StudentA" scope="singleton">
<property name="studentB" ref="b"></property>
</bean>
第二種,構造器註入
a類中持有b類的引用,並且a的構造函數參數中有b。實質上就是通過構造函數註入,創建a對象時要把b對象傳進去。
<bean id="a" class="com.qunar.pojo.StudentA">
<constructor-arg index="0" ref="b"></constructor-arg>
</bean>
第三種,靜態工廠
如果有需要靜態工廠實例化的類,不能通過靜態工廠.方法實現。在bean屬性中對應類指向靜態工廠,對應方法指向返回實例的方法
第四種,實例工廠
如果工廠不是靜態,需要實例化,就實例化對應工廠,設定factory-bean和factory-method進行方法調用。
``
設定三個實體類,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;
}
}
當我通過構造器註入時,會產生BeanCurrentlyInCreationException異常。為什麽會出現這種異常,spring如何加載實體?
Spring容器會將每一個正在創建的Bean 標識符放在一個“當前創建Bean池”中,Bean標識符在創建過程中將一直保持在這個池中,因此如果在創建Bean過程中發現自己已經在“當前創建Bean池”裏時將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對於創建完畢的Bean將從“當前創建Bean池”中清除掉。
Spring容器先創建單例StudentA,StudentA依賴StudentB,然後將A放在“當前創建Bean池”中,此時創建StudentB,StudentB依賴StudentC ,然後將B放在“當前創建Bean池”中,此時創建StudentC,StudentC又依賴StudentA, 但是,此時Student已經在池中,所以會報錯,因為在池中的Bean都是未初始化完的,所以會依賴錯誤.
解決這個問題,可以用setter註入的方式。
Spring是先將Bean對象實例化之後,再設置對象屬性。所以會先調用他的無參構造函數實例化。每個對象存在一個map中。當遇到依賴,就去map中調用對應的單例對象。
一部分源碼
另外: 對於“prototype”作用域Bean,Spring容器無法完成依賴註入,因為“prototype”作用域的Bean,Spring容器不進行緩存,因此無法提前暴露一個創建中的Bean。
Spring裝配Bean的過程
實例化;
設置屬性值;
如果實現了BeanNameAware接口,調用setBeanName設置Bean的ID或者Name;
如果實現BeanFactoryAware接口,調用setBeanFactory 設置BeanFactory;
如果實現ApplicationContextAware,調用setApplicationContext設置ApplicationContext
調用BeanPostProcessor的預先初始化方法;
調用InitializingBean的afterPropertiesSet()方法;
調用定制init-method方法;
調用BeanPostProcessor的後初始化方法;
Spring容器關閉過程
調用DisposableBean的destroy();
調用定制的destroy-method方法;
``
了解了bean默認是單例模式,不由想spring的單例和設計模式單例同一種嗎?其實不一樣。單例模式是指在一個JVM進程中僅有一個實例,而Spring單例是指一個Spring Bean容器(ApplicationContext)中僅有一個實例。如果有多個Spring容器,可能有多個Bean對象。
spring單例是一種類似註冊表實現的方式。利用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);
}
//保護的默認構造子
protected Singleton(){}
//靜態工廠方法,返還此類惟一的實例
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);
}
}
Spring Bean註入/單例理解/循環依賴