文章目錄
- 前言
- 一、Spring解決循環依賴時,為什么要使用三級緩存?
前言
??Spring解決循環依賴的手段,是通過三級緩存
:
- singletonObjects:存放所有生命周期完整的單例對象。(一級緩存)
- earlySingletonObjects:存放所有完成了
實例化
操作的早期單例對象。(二級緩存) - singletonFactories:存放單例工廠的對象,通過工廠創建早期Bean。(三級緩存)
??具體Spring是如何解決循環依賴問題的,在Spring循環依賴源碼分析中已經詳細說明,本篇側重于證明三級緩存的必要性。
一、Spring解決循環依賴時,為什么要使用三級緩存?
??這是一道非常經典的面試題。如果一個對象需要被代理,那首先需要生成一個普通的對象,而代理對象和普通對象是不能同時存在于容器中的,當一個對象需要使用代理的時候,就要使用代理對象覆蓋掉原來的普通對象。
??這是一個典型的循環依賴場景,存在兩個相互引用的 Bean:A 和 B。其中 A 包含 b 屬性,B 包含 a 屬性。無論是否存在循環依賴,這兩個 Bean 在完成實例化后都會自動存入三級緩存。需要注意的是,通過反射創建的實例對象與放入三級緩存工廠中的對象實際上是同一個引用。
??然后A對象執行屬性注入,發現需要B屬性,B在容器中是不存在的,于是需要去創建B對象。
??B對象在執行屬性注入,發現需要A屬性,需要從容器中獲取:
??這里從三級緩存
中,可以獲取到創建A對象的工廠方法,如果A對象需要AOP,則會:
??為B的A屬性賦值時,使用的是經過AOP處理的A對象。在B完成初始化后,A對象完成屬性注入并繼續初始化流程,此時不會再次進行AOP處理。這意味著A對象和B對象中的A屬性實際上指向同一個經過AOP處理的對象實例。這就是三級緩存存在的意義。
??如果僅使用二級緩存,給B的A屬性賦值的是未經AOP處理的原始A對象。隨后A繼續完成后續生命周期并經過AOP處理,導致最終生成的A對象與B持有的A對象不同——前者經過AOP增強,而后者仍是原始對象。
??這里可能會有一個疑惑的點,那就是,A對象和B中的A屬性應該是同一個實例,如果A繼續完成后續生命周期并執行AOP處理,最終將成為AOP增強對象。那么即便最初B獲取A屬性時,A尚未經過AOP處理,當A完成整個流程后,B中的A是否也會自動成為AOP增強對象?
??答案是否定的,因為Spring 中對 A 做 AOP 是通過“生成一個新的代理對象”,而不是修改原始 A 的引用本身:
- B 中的 a 是早期注入進去的,它的類型是原始 A
- Spring 后續創建了 proxyA,它是一個新對象,代理了原始 A
- 但此時 B 中早就注入好了,不會“回頭替換”那個引用
- 所以 B 中的 A 就是原始對象,沒有事務、沒有切面
??一個簡單案例即可證明:
public class MyService {public void doSomething() {System.out.println("Doing something...");}
}
@Aspect
public class LogAspect {@Before("execution(* MyService.*(..))")public void before() {System.out.println(">>> [AOP] Before method execution");}
}
public class AopTest {public static void main(String[] args) {MyService target = new MyService();// 創建代理工廠并添加切面AspectJProxyFactory factory = new AspectJProxyFactory(target);factory.addAspect(new LogAspect());// 獲取代理對象MyService proxy = factory.getProxy();System.out.println("target = " + target.getClass());System.out.println("proxy = " + proxy.getClass());System.out.println("是否同一引用: " + (target == proxy));proxy.doSomething();}
}