Spring5.0源碼深度解析之SpringBean循環依賴問題解決方案

Spring循環依賴

什麼是Spring的循環依賴?循環依賴會存在哪些問題?

示例:AService依賴BService; BService依賴AService

@Service
public class AService {
    //
    @Autowired
    public BService bService;
}
@Service
public class BService {
    @Autowired
    public AService aService;
}

上面演示的例子就是循環注入

如果改爲多例的化,運行時就會報錯,循環引用異常,找不到對象

@Scope("prototype")

Spring中的循環依賴問題在單例的情況下,Spring是已經幫我們解決好了,多例沒有解決循環依賴問題。

爲啥,多例的情況下 Spring沒有去解決循環依賴問題?

因爲在多例的情況下,設置的多例的對象沒有明確哪一個,就會產生循環依賴問題。

Spring多例我們要:如何解決循環依賴問題?

我們可以自己去:明確指定引用那個對象

@Service
@Scope("prototype")
public class AService {
   // @Autowired
    public BService bService;
    // 爲什麼Aservice在創建的時候 爲什麼Bservice比ASERVICE 先創建
    public AService() {
        System.out.println("AService被Java的反射技術創建");
    }
    public void setbService(BService bService) {
        this.bService = bService;
    }

}
@Service
@Scope("prototype")
public class BService {
   // @Autowired
    public AService aService;

    public void setaService(AService aService) {
        this.aService = aService;
    }
}
public class SpringApp {
    public static void main(String[] args) {
//        1. ioc容器在創建的時候所有的單例對象是不是會被創建
       AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
//        // 對例子情況 當你在調用的時候才獲取
        AService aSerivce = applicationContext.getBean("AService", AService.class);
        BService bSerivce = applicationContext.getBean("BService", BService.class);
        aSerivce.setbService(bSerivce);
        bSerivce.setaService(aSerivce);
        // 循環引用異常 找不到對象
        /**
         * 思考問題? 如果我們的項目對象必須要是多例? 而且必須要循環引用  明確的指定引用那個對象
         */
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }

SpringBean循環依賴三級緩存概念

思考問題:單例對象在什麼時候創建?

在IOC容器被創建的時候創建

多例的情況下,是在getbean()調用的情況下創建。多例對象每次用完就會去銷燬掉。

SpringBean Aservice對象被創建流程步驟源碼分析

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

public Object getBean(String name) throws BeansException {
    return this.doGetBean(name, (Class)null, (Object[])null, false);
}

public Object getSingleton(String beanName) {
    return this.getSingleton(beanName, true);
}

獲取緩存對象:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);    //根據BeanName去集合中查,查到就返回這個對象,【】【】【】一級緩存對象集合【】【】【】緩存完整對象【】【】完整對象表示對象已經創建完了,並且對象屬性已經賦值了。
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        Map var4 = this.singletonObjects;
        synchronized(this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);    //查詢二級緩存中是否有緩存對象
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);    //查詢三級緩存,三級緩存中有的化,將三級緩存中的數據放入二級緩存中
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

一級緩存沒有找到,就去找二級緩存中找

singletonObject == null && this.isSingletonCurrentlyInCreation(beanName) //一級緩存沒有,並且singletonsCurrentlyInCreation判斷是否之前標記爲該對象開始創建

if (isPrototypeCurrentlyInCreation(beanName)) { //我們對象是單例的,所有不進入
   throw new BeanCurrentlyInCreationException(beanName);
}

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   synchronized (this.singletonObjects) {
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         if (this.singletonsCurrentlyInDestruction) {
            throw new BeanCreationNotAllowedException(beanName,
                  "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                  "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
         }
         if (logger.isDebugEnabled()) {
            logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
         }
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            // Has the singleton object implicitly appeared in the meantime ->
            // if yes, proceed with it since the exception indicates that state.
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               throw ex;
            }
         }
         catch (BeanCreationException ex) {
            if (recordSuppressedExceptions) {
               for (Exception suppressedException : this.suppressedExceptions) {
                  ex.addRelatedCause(suppressedException);
               }
            }
            throw ex;
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

protected void beforeSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

標識爲該對象開始創建

最終調用

正真去創建我們的Bean對象:

既然要創建對象,先反射走無參構造函數,對象先實例化完成,在賦值

執行這個方法,輸出構造函數打印的語句,說明底層通過Java反射機制初始化的

在這之前,我們的對象屬於嬰兒對象,因爲它的屬性還沒有賦值。都是稱爲嬰兒對象。

那麼什麼時候賦值呢?

populateBean(beanName, mbd, instanceWrapper);//這裏給對象屬性賦值

在給對象屬性賦值之前:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {    //【】【】如果一級緩存沒有該對象的情況下,會將該對象存放在三級緩存中
         this.singletonFactories.put(beanName, singletonFactory);    //【】【】存放在三級緩存中,對象實例化完成,但是沒有賦值,嬰兒對象【】【】
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}

A對象堅持依賴B對象,這時候B對象也需要被創建

A對象已經存放在三級緩存中,這時候要去創建B對象

此時B對象也要走A對象流程

看下調用鏈

也將B對象放入三級緩存

總結下:

AService在創建的時候,提前曝光存放到三級緩存中,AService發現依賴BService,這時候Bservice提前曝光存放到三級緩存中去。

此時BService又依賴AService,此時BService經過賦值是完整對象,但是Aservice還是嬰兒對象,沒有完全創建完畢。

就會去把BService對象註冊到一級緩存中,同時會把之前緩存BService對象的二級緩存清除掉

AService對象依賴BService,BService此時已經創建成功了,那麼AService在設置屬性後,就直接把BService賦值給AService。

開始註冊AService對象

總結:

 SpringBean中 Aservic對象被創建流程步驟源碼分析:

  1. doGetBean創建我們bean對象
  2. getSingleton (beanName) 獲取緩存對象

注意:完整對象概念:對象已經實例化成功並且所有屬性都已經賦值

  • singletonObjects 一級緩存完整對象
  • earlySingletonObjects 二級緩存 緩存嬰兒對象
  • singletonFactories 三級緩存存放嬰兒對象

理解概念:

  1. 完整對象表示該對象實例化完成並且所有的屬性已經賦值。
  2. 嬰兒對象(提前對象)對象已經實例化完成但是屬性沒有賦值的。

singletonObject ==null&&this.singletonsCurrentlyInCreation.contains(beanName);

{

才能夠查詢二級緩存

}
singletonsCurrentlyInCreation :標記爲該對象開始創建

  1. getSingleton(String beanName, ObjectFactory<?> singletonFactory)

this.singletonsCurrentlyInCreation.add(beanName) 表示該對象已經開始創建

  1. createBean() →doCreateBean
  2. addSingletonFactory  將嬰兒對象(不完整對象也就是隻是實例化完成但是屬性沒有賦值的) 存放三級緩存中。
  3. A對象已經存放到三級緩存中,開始給對象屬性賦值的時候 需要創建B對象。

A對象檢查發現依賴B對象 這時候B對象也需要被創建

本文參考:

螞蟻課堂

http://www.mayikt.com/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章