解析spring循環依賴策略


循環依賴

所謂循環依賴就是多個Bean之間依賴關系形成一個閉環,例如A->B->C->...->A 這種情況,當然,最簡單的循環依賴就是2個Bean之間互相依賴:A->B(A依賴B), B->A(B依賴A) 。在Spring中,如果A->B,那么在創建A的過程中會去創建B,在創建B(或B的依賴)的過程中又發現B->A,這個時候就出現了循環依賴的現象。

循環依賴的解決

spring中的循環依賴只有當

  1. Bean是單例,
  2. 通過屬性注入的情況

這兩個條件滿足的情況下是沒問題的。但是如果是通過構造器依賴,或者不是單例模式的情況下循環依賴就會拋出異常BeanCurrentlyInCreationException。下面從代碼層面上解析一下為什么。

Prototype的循環依賴問題

為什么最先介紹Prototype的循環依賴呢,因為可以順便介紹在Spring中創建Bean的流程核心流程:在AbstractFoctory的doGetBean的方法。這個方法很長,這里只寫出核心邏輯,並在注解上注明了個人理解:

protected <T> T doGetBean(
        final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {
    
    final String beanName = transformedBeanName(name);
    Object bean;
    
    //嘗試獲取單例對象,因為spring大部分的bean都是單例的,所以這里先嘗試能否獲取。
    registered singletons.
    Object sharedInstance = getSingleton(beanName);
    //單例存在的情況下,那么beanName返回的肯定是單例類,但是這里還需要判斷是不是FactoryBean
    if (sharedInstance != null && args == null) {
        ...
        //FactoryBean應該返回getObject()對象
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    
    else {
        //走到這里,有可能beanName是單例模式,但之前並沒有實例化,或者是Prototype類型。
        //首先判斷不是循環依賴,這里的循環依賴指的是Prototype類型
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    
    
        try {
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 如果是單例,則創建單例模式
            if (mbd.isSingleton()) {
                // !!!這里是解決單例循環依賴的關鍵,后面再分析
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            throw ex;
                        }
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
    
            else if (mbd.isPrototype()) {
                // 原型模式,則創建一個新對象.
                Object prototypeInstance = null;
                try {
                    /*這里是Prototype循環依賴的問題,會記錄在map中beanName,
                    如果在解決當前Bean的依賴過程中還依賴當前Bean,
                    則說明了出現了循環依賴
                    */
                            beforePrototypeCreation(beanName);
                            prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    //對應beforePrototypeCreation(),從map中移除
                            afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            ...
        }
    }
    
    ...
    return (T) bean;
}

 

可以看出,該流程中就考慮了Prototype的循環依賴的問題,只要在創建Prototype的Bean中出現循環依賴那么就拋出異常。但是在singleton的情況下,則通過另外的方式來解決。

Singleton的循環依賴之構造注入

在上面介紹中,出現了一個很關鍵的地方:

sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    @Override
    public Object getObject() throws BeansException {
        try {
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            throw ex;
        }
    }
});

 

這個getSingleton涉及到了ObjectFactory這個接口類,這個接口的功能和FactoryBean類似,但是主要是用來解決循環依賴的。在初始化過程同決定返回的Singleton對象是。關於單例的對象的創建,又要介紹一下DefaultSingletonBeanRegistry這個類,這個類主要用來幫助創建單例模式,其中主要的屬性:

/** 緩存創建的單例對象: bean名字 --> bean對象 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** 緩存單例的factory,就是ObjectFactory這個東西,: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** 也是緩存創建的單例對象,功能和singletonObjects不一樣,
在bean構造成功之后,屬性初始化之前會把對象放入到這里,
主要是用於解決屬性注入的循環引用: bean name --> bean instance 
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** 記錄在創建單例對象中循環依賴的問題,還記得Prototype中又記錄創建過程中依賴的map嗎?
在Prototype中只要出現了循環依賴就拋出異常,而在單例中會嘗試解決 */
private final Set<String> singletonsCurrentlyInCreation =
        Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

 

現在回過來看getSingleton(beanName, new ObjectFactory<Object>()這個方法的實現。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

    synchronized (this.singletonObjects) {
        //嘗試在singletonObjects中獲取
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            //不存在則創建
            //把當前beanName加入到singletonsCurrentlyInCreation中
            beforeSingletonCreation(beanName);
            try {
                
                singletonObject = singletonFactory.getObject();
            }
            ...
            finally {
                ...
                //從singletonsCurrentlyInCreation中刪除beanName
                afterSingletonCreation(beanName);
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}

 

這段邏輯是不是和Prototype中解決循環類似,這里其實就是調用了ObjectFactory的getObject()獲取對象,回過頭去看前面代碼,ObjectFactory的getObject()方法實際調用的是createBean(beanName, mbd, args)。說到createBean(beanName, mbd, args)又不得不說AbstractAutowireCapableBeanFactory這個類,主要功能就是完成依賴注入的Bean的創建,這個類的createBean方法代碼如下,注意注解說明:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    ...
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    ...
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {

    // 實例化bean
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        //如果沒實例化則創建新的BeanWrapper
        //如果是通過構造器注入,這里是一個關鍵點
        /*
        因為在A初始化的時候發現構造函數依賴B,就會去實例化B,
        然后B也會運行到這段邏輯,構造函數中發現依賴A,
        這個時候就會拋出循環依賴的異常
        */
                instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    

   //如果當前是單例,並且allowCircularReferences為true(默認就是true,除非我們不希望Spring幫我們解決)
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        /*
        !!!這里很重要,把構造成功,但屬性還沒注入的
        的bean加到singletonFactory中,這樣再解決A的依賴
        過程中如果依賴A,就把這個半成品返回回去。
        */
        addSingletonFactory(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                return getEarlyBeanReference(beanName, mbd, bean);
            }
        });
    }

    
    Object exposedObject = bean;
    try {
        //自動注入屬性
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }
    ...

    return exposedObject;
}

 

 

注解已經注明了我的理解。就不再贅述

總結

上面代碼是我一邊debug一個寫下的,現在寫完了,根據自己的理解總結一下。

相關類說明

image

  • AbstractBeanFactory,這個類中包含了Bean創建的主要流程,在doGetBean這個方法中包含了對Prototype循環依賴處理。邏輯很簡單,出現了循環依賴則直接拋出異常
  • DefaultSingletonBeanRegister 用於管理Singleton的對象的創建,以及解決循環依賴的問題,其中解決循環依賴的關鍵屬性就是了earlySingletonObjects,他會在構造Singleton對象過程中暫時緩存構造成功,但屬性還未注入的對象,這樣就可以解決循環依賴的問題。
  • AbstractAutowireCapableBeanFactory,自動注入的相關邏輯,包自動注入的對象的創建、初始化和注入。但如果在調用構造函數中發現了循環依賴,則拋出異常
  • ObjectFactory,這個接口功能和FactoryBean類似,但是為了解決循環依賴,他決定了在獲取的getSingleton()是一個完成品還是一個半成品。

思考

如果A--構造依賴->B,B--屬性依賴-->A,例如:

@Component
public class BeanA {
    private BeanB beanB;

    @Autowired
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}

@Component
public class BeanB {
    @Autowired
    private BeanA beanA;
}

這種情況會異常嗎?提示:都有可能


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM