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

理解循環依賴問題,首先明白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);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章