Spring 循環依賴是指:兩個不同的 Bean 對象,相互成爲各自的字段,當這兩個 Bean 中的其中一個 Bean 進行依賴注入時,會陷入死循環,即循環依賴現象。
代碼例子:
@Componentpublic UserServiceA { @Autowire private UserServiceB userServiceB;}@Componentpublic UserServiceB { @Autowire private UserServiceA userServiceA;}UserServiceA 與 UserServiceB 之間相互依賴二、三級緩存解決循環依賴2.1 三級緩存針對循環依賴的現象,Spring 中使用提供了三級緩存解決循環的問題,我們先看 Spring 中使用到三級緩存的代碼:
public DefaultSingletonBeanRegistry implements SingletonBeanRegistry { /** * 一級緩存 */ private Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); /** * 二級緩存 */ private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); /** * 三級緩存 */ private Map<String, ObjectFactory<?>> singletonFactory = new HashMap<>(); @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }}SingletonObjects:一級緩存,存儲完整的 Bean;EarlySingletonObjects:二級緩存,存儲從第三級緩存中創建出代理對象的 Bean,即半成品的 Bean;SingletonFactory:三級緩存,存儲實例化完後,包裝在 FactoryBean 中的工廠 Bean;在上面的 getSingleton 方法中,先從 SingletonObjects 中獲取完整的 Bean,如果獲取失敗,就從 EarlySingletonObjects 中獲取半成品的 Bean,如果 EarlySingletonObjects 中也沒有獲取到,那麽就從 SingletonFactory 中,通過 FactoryBean 的 getBean 方法,獲取提前創建 Bean。如果 SingletonFactory 中也沒有獲取到,就去執行創建 Bean 的方法。
2.2 解決循環依賴Spring 産生一個完整的 Bean 可以看作三個階段:
createBean:實例化 Bean;populateBean:對 Bean 進行依賴注入;initializeBean:執行 Bean 的初始化方法;産生循環依賴的根本原因是:對于一個實例化後的 Bean,當它進行依賴注入時,會去創建它所依賴的 Bean,但此時它本身沒有緩存起來,如果其他的 Bean 也依賴于它自己,那麽就會創建新的 Bean,陷入了循環依賴的問題。
所以,三級緩存解決循環依賴的根本途徑是:當 Bean 實例化後,先將自己存起來,如果其他 Bean 用到自己,就先從緩存中拿,不用去創建新的 Bean 了,也就不會産生循環依賴的問題了。過程如下圖所示:
在 Spring 源碼中,調用完 createInstance 方法後,然後就把當前 Bean 加入到 SingletonFactory 中,也就是在實例化完畢後,就加入到三級緩存中;2.3 爲什麽需要三級緩存?一層和兩層可以嗎?2.3.1 一級緩存失效的原因對于只使用一級緩存的情況,是不能夠解決循環依賴的,有下面兩個原因:
如果只使用一級緩存,在創建 Bean 的過程中,我們會在初始化完畢(注意是初始化,如果只有一級緩存,該緩存需要存儲完整的 Bean)後把 Bean 放入到緩存中。此時依然會産生循環依賴問題,因爲依賴注入的過程是在初始化之前的,依賴注入時是從緩存中獲取不了對應的 Bean,從而再次引起循環依賴問題。那如果我們提前在 Bean 實例化後就放入到緩存呢?答案也是不行的。因爲我們沒有考慮到代理對象(Spring AOP) ,如果我們創建的 Bean 是代理對象,就要在實例化後就要創建出來,那麽就會帶來新的問題:JDK Proxy 代理對象實現的是目標類的接口,在進行依賴注入時會找不到對應的屬性和方法而報錯(也就是說,提前創建出來的代理對象是沒有原來對象的屬性和方法的)。Spring AOP 是依賴于 AnnotationAwareAspectJAutoProxyCreator 的,這是一個後置處理器,在 Bean 實例化完畢後在初始化方法中才執行的。
2.3.2 不使用二級緩存的原因先說結論,使用二級緩存是可以的。
對于普通對象來說,使用二級緩存是可以解決循環依賴的。在實例化後把對象存入第一級緩存中,如果其他對象依賴注入該對象,就從第一級緩存中拿就行了。對象初始化完畢後,再寫入到第二級緩存中即可。
但是對于代理對象來說,就顯得十分麻煩了。如果循環依賴注入的對象是代理對象,我們就需要在對象實例化後提前把代理對象創建出來,即提前創建出所有的代理對象。但是在目前 Spring AOP 的設計來說,代理對象的創建是在初始化方法中的 AnnotationAwareAspectJAutoProxyCreator 後置處理器創建的。這與 Spring AOP 的代理設計原則是相違背的。
所以,Spring 就再引用了一層緩存 SingletonFactory,存儲著 FactoryBean,我們來看看代碼:
if (beanDefinition.isSingleton()) { Object finalBean = bean; //加入FactoryBean addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));}當我們調用這個 FactoryBean 的 getBean 方法時:
protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) { Object exposedObject = bean; List<BeanPostProcessor> beanPostProcessors = getBeanPostProcessors(); for (BeanPostProcessor beanPostProcessor : beanPostProcessors) { if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { exposedObject = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).getEarlyBeanReference(bean, beanName); if (exposedObject == null) { return exposedObject; } } } return exposedObject;}// AnnotationAwareAspectJAutoProxyCreator@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) { earlyProxyReferences.add(bean); return wrapIfNecessary(bean,beanName);}發現它其實通過 InstantiationAwareBeanPostProcessor 接口的 getEarlyBeanReference 來創建代理對象。所以對于代理對象來說,Spring 沒有直接提前創建,而是在它産生循環依賴時,再通過 getEarlyBeanReference 方法來創建代理對象的。
三、循環依賴被完全解決了嗎3.1 循環依賴只支持單例對象對于 scope 爲 property 的 Bean,三級緩存是沒有解決循環依賴的。因爲它們的作用域是原型,每次使用到時都會創建一個新對象,不進緩存!
@Override protected Object createBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException { Object bean = null; try { // 加入一級緩存 if (beanDefinition.isSingleton()) { Object finalBean = bean; addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean)); } } catch (Exception e) { throw new BeansException("Instantiation of bean failed", e); } Object exposeObject = bean; if (beanDefinition.isSingleton()) { // 從二級緩存中獲取對象 exposeObject = getSingleton(beanName); registerSingleton(beanName, exposeObject); } return exposeObject;}可見,在加入緩存時,都會判斷當前的 Bean 是不是 Singleton 的,如果不是就不加入到緩存中。3.2 通過構造器注入的類無法解決循環依賴@Componentpublic BeanB { private BeanA a; public BeanB(BeanA a) { this.a = a; }}@Componentpublic BeanA { private BeanB b; public BeanA(BeanB b) { this.b = b; }}上述代碼中,我們通過構造器來注入 BeanA 和 Bean B,Spring 是無法解決它們循環依賴的問題的。
因爲在調用 BeanA 的構造器方法時 BeanA 是沒有實例化完成的,緩存中不存在 BeanA 對象,此時就要注入 BeanB 對象,毫無疑問的會産生循環依賴的問題。
但是,對于這種情況來說,我們可以通過 @Lazy 注解來延遲 BeanB 的加載。即調用構造器方法時先不創建 BeanB 對象,當使用到BeanB 對象時才繼續創建,此時 BeanB 也已經創建完成了,就不會産生循環依賴的問題了。
@Componentpublic BeanA { private BeanB b; public BeanA(@Lazy BeanB b) { this.b = b; }}