1. 程式人生 > >Spring Bean註入/單例理解/循環依賴

Spring Bean註入/單例理解/循環依賴

single cat 問題 保持 poj sin cep 返回 win

理解循環依賴問題,首先明白spring有四種註入方式。

第一種,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註入/單例理解/循環依賴