問題描述
spring循環依賴是對于ioc容器。類A、B、C,類A依賴了B,類A依賴了C,類B依賴了A,類C依賴了A。假如現在類A需要放到ioc,屬性賦值的時候會去找B這個bean,但是B不存在,于是去創建B這個bean,但是實例化beanB之后再屬性賦值的時候需要注入A這個bean。這樣就造成了循環。
解決辦法
spring通過三級緩存解決循環依賴問題,首次注入beanA的時候會在類A實例化之后,spring會創建一個ObjectFactory用來后續獲取beanA的早期引用,并將這個ObjectFactory放到三級緩存中。接著注入beanB,初始化類B之后屬性賦值,需要注入beanA,這是spring會去三級緩存中獲取A的ObjectFactory調用getObject()獲取A的早期引用,如果A需要被代理(如@Transactional),會在此時去創建A的代理對象,放到二級緩存中,然后刪除三級緩存的A的工廠對象。此時returnA這時循環有了出口,就解決了循環依賴。
補充
時序圖
- 實例化->new A(這里把創建A的ObjectFactory放到三級緩存)
- 屬性賦值(循環依賴的時候這里去創建AOP代理)
- 初始化(正常這里創建aop代理)
三級緩存是為了支持aop代理的按需創建。二級緩存的話就是直接把早期對象放到二級緩存,實例化A之后馬上把他放到二級緩存。如果直接把早期對象放到二級緩存,(實例化之后)就無法靈活決定是否創建代理(因為AOP代理是在postProcessAfterInitialization(上圖初始化中)中創建的)。可能導致重復創建代理或代理失效。
而如果是三級緩存的話
spring創建一個bean的階段
1.實例化(instantiation) ->new A()
2.屬性注入(Populate Properties) ->setB(),setC()
3.初始化(Initialization) ->調用init-method,BeanPostProcessorpostProcessBeforeInitialization//是BeanPostProcessor的方法初始化方法(@PostConstruct,init-method)postProcessAfterInitialization ->AOP代理就在這里創建!//是BeanPostProcessor的方法
4.放入一級緩存(singletonObject)
Spring 提供了 InstantiationAwareBeanPostProcessor.getEarlyBeanReference() 方法,允許 AOP 模塊在早期引用階段就決定是否創建代理。這個方法會在 ObjectFactory.getObject() 被調用時觸發,是提前創建代理的關鍵入口
代碼細節
三級緩存:singletonFactories如下
Map<String, ObjectFactory<?>> singletonFactories
其中鍵是類名首字母小寫。