Spring源碼-循環依賴源碼解讀


Spring源碼-循環依賴源碼解讀


  筆者最近無論是看書還是從網上找資料,都沒發現對Spring源碼是怎么解決循環依賴這一問題的詳解,大家都是解釋了Spring解決循環依賴的想法(有的解釋也不准確,在《Spring源碼深度解析》作者也是看別人的博客說明了一下),沒有從源碼的角度分析是怎么解決循環依賴的,筆者就把自己看源碼的過程寫一下。

  寫這一篇文章算是個引路的,Spring為了程序的健壯性做了大量分析校驗,調用的方法繁多復雜,我這篇文章為讀者清理出解決循環依賴的流程。

Spring中對象可以配置成單例模式也可配置為原型模式(原型模式很值得一看)。
Spring中可以通過構造函數注入、setter注入的方式來解決對象與對象間的依賴。
對象間的循環依賴只能配置單例、setter注入的方式來解決,其他方法就會報錯,下面我們通過源碼分析一下。

一、單例、setter注入解決循環依賴

假如有TestA、TestB、TestC三個對象,其中TestA依賴TestB,TestB依賴TestC,TestC依賴TestA。

下面具體通過代碼分析Spring是如何解決單例通過Setter注入的循環依賴。
在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry類中有幾個集合類型的成員變量,用來做緩存用的需要特別留意,源碼如下:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
        ......
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    private final Map<String, Object> earlySingletonObjects = new HashMap(16);
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
    ......
}

上面的代碼中:
singletonsCurrentlyInCreation:保存對象的BeanName,在創建對象之前就會把對象的beanName保存起來。
singletonFactories:保存對象的BeanName和創建bean的工廠AbstractAutowireCapableBeanFactory(ObjectFactory),(對象的構造函數是在這一步完成的)
earlySingletonObjects:保存對象BeanName和對象的早期實例(ObjectFactory#getObject得到的對象)(此時對象還沒注入屬性),此時可以作為對象填充依賴。
singletonObjects:保存BeanName和bean的實例(此時對象已經完成了屬性注入)

通過Spring獲取testA對象:

通過Spring獲取testA對象
ApplicationContext factory=new ClassPathXmlApplicationContext("classpath:applicationContext-beans.xml");
TestA testA = (TestA) factory.getBean("testA");
System.out.println(testA);

factory.getBean最終調用的是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

下面看doGetBean源碼:

protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
  ......
  //重點1
  Object sharedInstance = this.getSingleton(beanName);
  
  ......
  
    if (mbd.isSingleton()) {
        //重點2
        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                try {
                    //重點3
                    return createBean(beanName, mbd, args);
                }
                catch (BeansException ex) {
                    // Explicitly remove instance from singleton cache: It might have been put there
                    // eagerly by the creation process, to allow for circular reference resolution.
                    // Also remove any beans that received a temporary reference to the bean.
                    destroySingleton(beanName);
                    throw ex;
                }
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
}

在上面源碼中:

1、重點1:

  //重點1 根據beanName試圖從緩存中獲取已經創建的對象,第一次進入是肯定返回null,這個函數放在后面解釋。

2、重點2:

  在//重點2中才是真正創建TestA對象的方法,下面是//重點2 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton的源碼:

//重點2
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
        ......
        //重點2-1
        beforeSingletonCreation(beanName);
        ......
        try {
            //重點2-2
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
        }catch (IllegalStateException ex) {
            ......
        }catch (BeanCreationException ex) {
            ......
        }
        finally {
            //重點2-3
            afterSingletonCreation(beanName);
        }
    
    }
}

  在//重點2-1中beforeSingletonCreation方法中只做了一件事,就是保存beanName到singletonsCurrentlyInCreation(注意),這個時候testA就保存在singletonsCurrentlyInCreation里面了,源碼如下:

//重點2-1
protected void beforeSingletonCreation(String beanName) {
    //this.singletonsCurrentlyInCreation.add(beanName)
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

  在 //重點2-2 會調用 //重點3 。

3、重點3:

  在//重點3中org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean調用doCreateBean,doCreateBean方法源碼:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    ......
    //重點3-1
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        if (logger.isDebugEnabled()) {
            logger.debug("Eagerly caching bean '" + beanName +
                    "' to allow for resolving potential circular references");
        }
        //重點3-2
        addSingletonFactory(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                //重點3-3
                return getEarlyBeanReference(beanName, mbd, bean);
            }
        });
    }
    ......
  
}

  在//重點3-1判斷testA必須是單例,並存在在singletonsCurrentlyInCreation中,此時才會調用//重點3-2的addSingletonFactory方法,//重點3-2的addSingletonFactory方法源碼,

//重點3-2
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
        
            //重點3-2-1
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            
            this.registeredSingletons.add(beanName);
        }
    }
}

  在上面的代碼中有一個singletonFactory的參數,這個參數是//重點3-3調用getEarlyBeanReference得到的,getEarlyBeanReference返回的就是ObjectFactory對象,是完成構造方法的。

  在//重點3-2-1 向singletonFactories添加 ObjectFactory(注意),這個時候,testA和testA的ObjectFactory對象保存在singletonFactories,並移除earlySingletonObjects(現在earlySingletonObjects里面並沒有testA)。

  執行完//重點3-2,發現testA依賴TestB對象,此時會遞歸調用getBean獲取TestB,testB執行步驟和上面testA一樣,然后testB依賴TestC,遞歸調用TestC,此時singletonFactories里面保存的數據如下:
  testA -> ObjectFactory(TestA)
  testB -> ObjectFactory(TestB)
  testC -> ObjectFactory(TestC)

  創建testC過程中執行完//重點3-2,發現依賴testA,此時會遞歸調用getBean獲取TestA,這時候執行到//重點1,下面開始解析//重點1 Object sharedInstance = getSingleton(beanName);方法。

4、重點1:

  下面是調用//重點1 getSingleton(beanName),調用getSingleton(beanName, true)的源碼:

//重點1
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                //重點1-1
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                
                    //重點1-2
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

  在上面代碼中isSingletonCurrentlyInCreation就是判斷對象(testA)是否被保存過(在//重點2-1的時候testA就被保存了)

  在//重點1-1中,從singletonFactories緩存中獲取到ObjectFactory(TestA),

  在//重點1-2並通過模板模式獲取TestA對象,保存在earlySingletonObjects緩存中,並移除singletonFactories中的testA,

  此時testC獲取到TestA的早期對象,可以注入TestA對象,自此TestC完成依賴注入,並把testC保存到singletonObjects中。

  TestC創建完成,返回給testB,testB也算是完成了創建,然后返回給testA,自此循環依賴算是完成了。

總結:

  單例的setter注入,會先把創建testA、testB、testC對象的ObjectFactory(對象工廠)保存在singletonFactories里面,然后testC依賴testA,那就從singletonFactories緩存中拿到testA的ObjectFactory,通過ObjectFactory的getObject獲取TestA的對象,並保存在earlySingletonObjects緩存中,清除singletonFactories緩存中的testA,此時testC就可以獲取earlySingletonObjects緩存中TestA的對象,完成注入TestA的過程。TestC對象創建完成就可以注入到TestB對象中,然后TestB注入到TestA中。

  singletonsCurrentlyInCreation就是用來保存是否試圖創建某個對象的beanName,不管有沒有創建成功,為后來從singletonFactories緩存中或earlySingletonObjects緩存中取值做個標識。

二、單利、構造函數注入循環依賴

   假如有TestA、TestB兩個對象,TestA依賴TestB,TestB依賴TestA;

  構造函數注入和setter注入的不同在於,構造函數注入無法先調用構造函數實例化對象,當TestA依賴TestB,會先把testA保存到singletonsCurrentlyInCreation中,然后getBean("testB"),然后把testB保存到singletonsCurrentlyInCreation中,發現TestB依賴TestA,然后再getBean("testA"),此時執行下面的代碼(和模塊一,重點2是同一塊代碼):

//重點2 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton的源碼:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    //重點2-0
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
        ......
        //重點2-1
        beforeSingletonCreation(beanName);
        ......
        try {
            //重點2-2
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
        }catch (IllegalStateException ex) {
            ......
        }catch (BeanCreationException ex) {
            ......
        }
        finally {
            //重點2-3
            afterSingletonCreation(beanName);
        }
    
    }
}

 

  在上面的代碼中 //重點2-0  Object singletonObject = this.singletonObjects.get(beanName); 此時singletonObject為空會執行beforeSingletonCreation方法,源碼如下:

//重點2-1
protected void beforeSingletonCreation(String beanName) {
    //this.singletonsCurrentlyInCreation.add(beanName)
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

  結果發現singletonsCurrentlyInCreation已經存在testA,拋出BeanCurrentlyInCreationException

setter注入為什么不會執行這一步呢,因為setter注入中會實例化TestA、TestB保存在緩存中,所以在執行 //重點2-0  Object singletonObject = this.singletonObjects.get(beanName); 時 singletonObject 不為空,並不會執行beforeSingletonCreation所以不會保存。

三、原型模式下循環依賴

  假如有TestA、TestB兩個對象,TestA依賴TestB,TestB依賴TestA,當調用getBean("testA")時,會先把beanName(testA)保存到isPrototypeCurrentlyInCreation里面,發現TestA依賴TestB,就會去getBean("testB"),然后把beanName(testB)也保存到isPrototypeCurrentlyInCreation里面,此時TestB發現依賴TestA,去getBean("testA")時,發現isPrototypeCurrentlyInCreation已經存在testA,就會拋出BeanCurrentlyInCreationException異常,具體代碼如下

  factory.getBean最終調用的是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,

下面看doGetBean源碼:

protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
  ......
  if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
  ......
}

//isPrototypeCurrentlyInCreation源碼
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}

 


免責聲明!

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



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