Spring IOC、AOP以及Spring-Bean循環依賴解決

Spring IOC原理

IOC原理:

IOC,即控制反轉(Inversion of Control)。
在沒有引入IOC容器之前,對象A如果依賴於對象B,對象A要使用對象B的時候,就必須自己去創建對象B或者使用已經創建的對象B。無論創建還是使用對象B,控制權都在對象A自己手上。

引入IOC容器後,對象A與對象B之間失去了直接聯繫,當對象A運行到需要使用對象B的時候,由IOC容器創建一個對象B注入到對象A中,將創建管理依賴對象的控制權轉移到IOC容器中來,從而實現對象之間的解耦。

Spring IOC主要分爲兩個部分:

  • 初始化

在這裏插入圖片描述

  • 注入依賴

在這裏插入圖片描述

所以什麼是控制反轉,總結起來就是一句話,獲得依賴對象的控制權由自身轉移到了第三方容器上(IOC容器)

依賴注入(DI,Dependency Injection)是IOC原理的一種實現方式。在IOC容器運行期間,將依賴關係動態注入到對象之中。

依賴注入(DI)的三種方式

  • 構造方法注入
  • setter方法注入
  • 基於註解的注入

Spring-Bean循環依賴以及解決方式

什麼是循環依賴

循環依賴就是兩個或者兩個以上的Bean互相依賴對象,形成一個閉環。
如下圖,A依賴B,B依賴C,C又依賴於A,這樣依賴就形成了閉環,產生了循環依賴問題。

在這裏插入圖片描述

Spring中循環依賴的場景:

  • 構造器的循環依賴(無法解決,直接拋出BeanCurrentlyInCreationException異常)
  • Field屬性的循環依賴

Spring怎麼解決循環依賴

原理

基於Java的引用傳遞,當我們獲取到對象的引用時,對象的field或則屬性是可以延後設置的(但是構造器必須是在獲取引用之前)。即原始bean對象被創建後其屬性都爲null,屬性值可以延後設置。

Spring單例對象初始化過程

主要分三步:

  1. createBeanInstance:實例化,其實也就是調用對象的構造方法實例化對象(bean對象屬性爲null)

  2. populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充

  3. initializeBean:調用spring xml中的init 方法。

在這裏插入圖片描述

循環依賴問題主要出現在第一步(構造器循環依賴)和第二步(filed屬性循環依賴)

Spring爲了解決循環依賴問題,使用了三級緩存。

三級緩存

/** 單例對象的cache: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** 提前曝光的單例對象的cache: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** 單例工廠的cache: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

假設A依賴B,B依賴A

Spring在A初始化後且未注入field時,會先將原始A(屬性爲null)放入到singletonFactories中(預防循環依賴)。
開始注入filed時,發現A依賴B,但在三級緩存中都沒有發現B。於是開始創建B。

Spring開始創建B。同樣在B初始化後且未注入field時,會先將原始B(屬性爲null)放入到singletonFactories中。然後開始注入filed,發現B依賴A,此時在第三級緩存中發現了A,於是將A從第三級緩存singletonFactories中移動到第二級緩存earlySingletonObjects中,並且注入到B中。B完成創建放入第一級緩存singletonObjects中。於是A也完成了創建並放入到第一級緩存。

至此,循環依賴問題被解決。

Spring爲什麼不使用兩級緩存

將三級緩存放入二級緩存的時候,會判斷是否有SmartInstantiationAwareBeanPostProcessor這樣的後置處理器,換句話說這裏是給用戶提供接口擴展的,所以採用了三級緩存。

AOP

使用AOP的目的

將那些與業務無關,卻被多個業務模塊所共同調用的邏輯代碼封裝起來,減少系統的重複代碼,提高代碼的可維護性。

AOP使用場景

權限控制、日誌記錄、異常處理、數據校驗、事務管理等

AOP原理

AOP思想的實現一般是基於代理模式,主要的實現技術有Spring AOP和AspectJ

  1. AspectJ
    AspectJ採用的是靜態代理,在業務代碼編譯時將AOP代碼織入,相比Spring AOP動態代理技術,性能更好。

  2. Spring AOP
    Spring AOP採用的是動態代理,在運行期間對業務代碼進行增強。

    對於動態代理技術,Spring AOP提供了對JDK動態代理的支持和CGLIB的支持。

    JDK動態代理只能爲實現了接口的類創建動態代理實例。首先生成一個實現了代理接口的動態代理類,然後通過反射機制獲得動態代理類的構造函數並生成一個動態代理類的實例對象,在調用具體方法前調用invokeHandler方法來處理。

    如果目標對象的實現類沒有實現接口,Spring AOP將會採用CGLIB來生成AOP代理類。

Spring AOP實現

每個Spring Bean會有多個“方法攔截器”。注意:攔截器分爲兩層,外層由 Spring 內核控制流程,內層攔截器是用戶設置,也就是 AOP。

當代理方法被調用時,先經過外層攔截器,外層攔截器根據方法的各種信息判斷該方法應該執行哪些“內層攔截器”。內層攔截器的設計就是職責連的設計。
是不是賊簡單。

AOP實現主要分兩步:

  • 第一:代理的創建;
  • 第二:代理的調用。
  1. 代理的創建(按步驟):
    • 首先,需要創建代理工廠,代理工廠需要3個重要的信息:攔截器數組,目標對象接口數組,目標對象。

    • 創建代理工廠時,默認會在攔截器數組尾部再增加一個默認攔截器 —— 用於最終的調用目標方法。

    • 當調用 getProxy 方法的時候,會根據接口數量大餘 0 條件返回一個代理對象(JDK or Cglib)。

注意:創建代理對象時,同時會創建一個外層攔截器,這個攔截器就是 Spring 內核的攔截器。用於控制整個 AOP 的流程。

  1. 代理的調用

    • 當對代理對象進行調用時,就會觸發外層攔截器。

    • 外層攔截器根據代理配置信息,創建內層攔截器鏈。創建的過程中,會根據表達式判斷當前攔截是否匹配這個攔截器。而這個攔截器鏈設計模式就是職責鏈模式。

    • 當整個鏈條執行到最後時,就會觸發創建代理時那個尾部的默認攔截器,從而調用目標方法。最後返回。
      在這裏插入圖片描述

主要參考文章

Spring-bean的循環依賴以及解決方式

Spring-ioc核心源碼學習

Spring IOC 容器源碼分析系列文章

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