Spring框架解決迴圈依賴主要透過三級快取來實現,這主要發生在Spring容器建立bean的過程中。以下是Spring解決迴圈依賴的基本步驟:
一級快取(singletonObjects)
:存放已經建立好的單例物件,供其他bean引用。二級快取(earlySingletonObjects)
:存放原始的bean物件,即已經填充了屬性(依賴注入完成)但尚未經過初始化(呼叫afterPropertiesSet方法)的bean物件。三級快取(singletonFactories)
:存放bean工廠物件(BeanFactoryObjectProvider),用於解決迴圈依賴。當Spring容器建立bean時,如果檢測到迴圈依賴,它會將bean的早期引用(early reference)存入三級快取中。
具體步驟如下:
建立Bean
:當Spring容器建立一個bean時,首先會檢查該bean是否已經存在於一級快取中。如果存在,則直接使用;如果不存在,則繼續建立。註冊Bean
:在bean建立過程中,Spring會將bean的早期引用(未初始化的bean物件)註冊到二級快取中。處理迴圈依賴
:如果bean A依賴於bean B,而bean B又依賴於bean A,當Spring容器建立bean A時,會嘗試建立bean B。此時,如果bean B尚未完全建立,Spring容器會從二級快取中獲取bean B的早期引用,並將其注入到bean A中。使用三級快取
:在bean B的建立過程中,如果檢測到對bean A的依賴,Spring容器會從三級快取中獲取bean A的早期引用,並將其注入到bean B中。這樣,即使bean A尚未完全建立,bean B也可以繼續建立。初始化Bean
:當所有依賴都注入完成後,Spring容器會呼叫bean的afterPropertiesSet方法進行初始化,並將bean從二級快取移動到一級快取中。完成建立
:此時,bean A和bean B都已完成建立,並且相互引用,迴圈依賴問題得到解決。
需要注意的是,Spring只能解決單例作用域的bean的迴圈依賴問題,對於原型作用域的bean,Spring無法處理迴圈依賴。
下面V哥將透過一個簡單的示例來解釋Spring如何解決迴圈依賴的問題,並結合一個業務場景進行說明。
假設我們有一個電商系統,其中有兩個元件:OrderService(訂單服務)和PaymentService(支付服務)。這兩個服務相互依賴:OrderService需要使用PaymentService來處理支付,而PaymentService又需要OrderService來確認訂單狀態。
首先,我們定義這兩個服務的介面和實現類。
OrderService.java
public class OrderService { private PaymentService paymentService; public void setPaymentService(PaymentService paymentService) { this.paymentService = paymentService; } public void createOrder() { // 建立訂單邏輯 paymentService.processPayment(); } }
PaymentService.java
public class PaymentService { private OrderService orderService; public void setOrderService(OrderService orderService) { this.orderService = orderService; } public void processPayment() { // 處理支付邏輯 orderService.confirmOrderStatus(); } }
在這兩個類中,OrderService透過setter方法注入了PaymentService,而PaymentService也透過setter方法注入了OrderService,這就形成了一個迴圈依賴。
接下來,我們配置Spring容器,將這兩個bean定義為單例(singleton)作用域。
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="orderService" class="com.example.OrderService" scope="singleton"/> <bean id="paymentService" class="com.example.PaymentService" scope="singleton"/> </beans>
現在,當Spring容器啟動時,它會建立這兩個bean。由於它們之間存在迴圈依賴,Spring將使用三級快取來解決這個問題:
建立OrderService Bean
:Spring首先嚐試建立OrderService bean,並將其註冊到二級快取中。注入PaymentService
:在建立OrderService的過程中,Spring需要注入PaymentService。由於PaymentService尚未完全建立,Spring會嘗試從二級快取中獲取PaymentService的早期引用。建立PaymentService Bean
:此時,Spring開始建立PaymentService bean,並將其註冊到二級快取中。注入OrderService
:在建立PaymentService的過程中,Spring需要注入OrderService。由於OrderService已經在二級快取中,Spring會將其早期引用注入到PaymentService中。完成初始化
:一旦所有依賴都注入完成,Spring會呼叫每個bean的afterPropertiesSet方法(如果有的話),然後將它們從二級快取移動到一級快取中。解決迴圈依賴
:由於Spring使用了三級快取來儲存bean的早期引用,即使在bean完全建立之前,也可以將它們注入到其他bean中,從而解決了迴圈依賴問題。
透過這個示例,我們可以看到Spring如何解決迴圈依賴的問題,並確保了OrderService和PaymentService可以正常工作,即使它們之間存在相互依賴的關係。這種機制使得Spring容器可以靈活地處理複雜的依賴關係,而不需要開發者手動干預。
透過這種方式,Spring框架有效地解決了單例bean的迴圈依賴問題,確保了依賴注入的順利進行。