循環依賴,是依賴關係造成了一個圓環。好比:A對象有一個屬性B,那麼這時候咱們稱之爲A依賴B,若是這時候B對象裏面有一個屬性A。那麼這時候A和B的依賴關係就造成了一個循環,這就是所謂的循環依賴。若是這時候IOC容器建立A對象的時候,發現B屬性,而後建立B對象,發現裏面有A屬性,而後建立B.....這麼無限循環下去。咱們先用代碼演示一下:java
public class A { private B b=new B(); } public class B { private A a=new A(); } public class Test { public static void main(String[] args) { A a = new A(); } }
運行一下結果spring
那麼咱們能夠看到循環依賴存在的問題了緩存
- 棧內存溢出
- 程序的維護性和擴展性太差
顯然這種思路是不正確的。app
- 在容器中建立的對象是單例的
- 對象是循環依賴
若是咱們本身寫的話,該如何解決的呢?ide
public class A { private B b; public void setB(B b) { this.b = b; } } public class B { private A a; public void setA(A a) { this.a = a; } } public class Test { public static void main(String[] args) { A a = new A();//建立a對象 B b = new B();//由於a對象依賴B,那麼建立B b.setA(a);//建立B對象的時候,發現依賴A,那麼把經過構造方法生成的對象a賦值給B a.setB(b);//而後把生成的b對象注入到a裏面 } }
當使用Spring的 @Autowired 註解的時候,其實Spring的實現原理和上面很類似,先經過生成相關的對象,而後再把裏面須要依賴的對象設置進去。源碼分析
咱們如今從Spring源碼來走一遍。。測試
咱們現貼出最基本的測試代碼ui
@Component public class A { @Autowired B b; } @Component public class B { @Autowired A a; } public class RecyclerTest { @Test public void test() { ApplicationContext context = new AnnotationConfigApplicationContext("com.kailaisi.demo.recycler"); //getbean得時候才進行IOC容器中的對象的實例化工做 A a = (A) context.getBean("a"); } }
在咱們以前發佈的SpringBoot啓動流程源碼分析裏面,咱們提到過bean單例的生成是在Spring容器建立過程當中來完成的。通過多層的調用,最終會調用到 doGetBean 這個方法裏面。this
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { ... Object bean; //先從緩存中獲取是否認義了對應的類,這裏的緩存包括了半成品類緩存(只生成了類,可是尚未進行屬性注入的類)和成品類緩存(已經完成了屬性注入的類) Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { ...... //若是符合條件,直接從對飲給的bean單例中獲取到對象,而後返回 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { ... try { ..... //建立單例bean,解決循環依賴的根本方案 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { //調用建立單例的方法 return createBean(beanName, mbd, args); } ... }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } ... return (T) bean; } @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { ... //進行bean的建立 Object beanInstance = doCreateBean(beanName, mbdToUse, args); ... } protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { //bean的包裝類 BeanWrapper instanceWrapper = null; ... if (instanceWrapper == null) { //建立beanDefinition所對應的的參數的bean實例,這裏經過構造方法或者工廠方法或者cglib建立了對象 instanceWrapper = createBeanInstance(beanName, mbd, args); } if (earlySingletonExposure) { //將對象放到registeredSingletons隊列中,並從earlySingletonObjects中移除 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); ... //注入A的依賴,這裏面會發現屬性,而後從doGetBean()方法開始,生成B對象,而後循環走到這裏的時候,在隊列裏面會同時存在A對象和B對象。而後B對象注入A成功,返回後將生成的B注入到A,此時完成了A和B的對象生成,並解決了循環依賴問題 populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } ... }
加載過程比較長,其實主要是在加載的過程當中將對象的建立過程進行了分類處理,在建立的不一樣時期,放入到隊列來進行區分。code
- singletonObjects:單例對象列表
- singletonFactories:單例工廠隊列,對象剛開始建立的時候,會放入到這個隊列。
- earlySingletonObjects:產生了循環依賴的對象隊列,對象在建立以後,進行注入過程當中,發現產生了循環依賴,那麼會將對象放入到這個隊列,而且從singletonFactories中移除掉。
- singletonsCurrentlyInCreation:正在建立的對象隊列,整個建立過程都存放在這個隊列裏面,當完成了全部的依賴注入之後,從這個隊列裏面移除
- registeredSingletons:已經建立成功的單例列表。
知道了這幾個隊列之後,咱們能夠來整理測試例子中,A和B對象是如何一步步建立,並解決其循環依賴的問題了。
上述就是spring解決循環依賴的總體過程,跟咱們以前的那個方法很類似,只是對於各類狀況的處理更仔細。並且從這個過程也能理解spring對於對象的建立過程。
本文由 開了肯 發佈!