欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

Spring中循環(huán)依賴的解決方法

這篇文章主要介紹“Spring中循環(huán)依賴的解決方法”,在日常操作中,相信很多人在Spring中循環(huán)依賴的解決方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Spring中循環(huán)依賴的解決方法”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

成都創(chuàng)新互聯(lián)主要為客戶提供服務(wù)項目涵蓋了網(wǎng)頁視覺設(shè)計、VI標(biāo)志設(shè)計、成都營銷網(wǎng)站建設(shè)、網(wǎng)站程序開發(fā)、HTML5響應(yīng)式重慶網(wǎng)站建設(shè)公司、移動網(wǎng)站建設(shè)、微商城、網(wǎng)站托管及網(wǎng)站維護(hù)、WEB系統(tǒng)開發(fā)、域名注冊、國內(nèi)外服務(wù)器租用、視頻、平面設(shè)計、SEO優(yōu)化排名。設(shè)計、前端、后端三個建站步驟的完善服務(wù)體系。一人跟蹤測試的建站服務(wù)標(biāo)準(zhǔn)。已經(jīng)為成都石雕行業(yè)客戶提供了網(wǎng)站推廣服務(wù)。

一、先說說Spring解決的循環(huán)依賴是什么

Java中的循環(huán)依賴分兩種,一種是構(gòu)造器的循環(huán)依賴,另一種是屬性的循環(huán)依賴。

構(gòu)造器的循環(huán)依賴就是在構(gòu)造器中有屬性循環(huán)依賴,如下所示的兩個類就屬于構(gòu)造器循環(huán)依賴:

@Servicepublic class Student {  @Autowired  private Teacher teacher;  public Student (Teacher teacher) {    System.out.println("Student init1:" + teacher);  }  public void learn () {    System.out.println("Student learn");  }}

@Servicepublic class Teacher {  @Autowired  private Student student;  public Teacher (Student student) {    System.out.println("Teacher init1:" + student);  }  public void teach () {    System.out.println("teach:");    student.learn();  }}

這種循環(huán)依賴沒有什么解決辦法,因為JVM虛擬機在對類進(jìn)行實例化的時候,需先實例化構(gòu)造器的參數(shù),而由于循環(huán)引用這個參數(shù)無法提前實例化,故只能拋出錯誤。

Spring解決的循環(huán)依賴就是指屬性的循環(huán)依賴,如下所示:

@Servicepublic class Teacher {  @Autowired  private Student student;  public Teacher () {    System.out.println("Teacher init1:" + student);  }  public void teach () {    System.out.println("teach:");    student.learn();  }  }

@Servicepublic class Student {  @Autowired  private Teacher teacher;  public Student () {    System.out.println("Student init:" + teacher);  }  public void learn () {    System.out.println("Student learn");  }}

測試掃描類:

@ComponentScan(value = "myPackage") public class ScanConfig {  }

測試啟動類:

public class SpringTest {  public static void main(String[] args) {    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);    applicationContext.getBean(Teacher.class).teach();  }}

測試類執(zhí)行結(jié)果:

Student init:null Teacher init:null teach: Student learn

可以看到,在構(gòu)造器執(zhí)行的時候未完成屬性的注入,而在調(diào)用方法的時候已經(jīng)完成了注入。下面就一起看看Spring內(nèi)部是在何時完成的屬性注入,又是如何解決的循環(huán)依賴。

二、循環(huán)依賴與屬性注入

1、對于非懶加載的類,是在refresh方法中的 finishBeanFactoryInitialization(beanFactory) 方法完成的包掃描以及bean的初始化,下面就一起追蹤下去。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // 其他代碼  // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }

可以看到調(diào)用了beanFactory的一個方法,此處的beanFactory就是指我們最常見的那個DefaultListableBeanFactory,下面看它里面的這個方法。

2、DefaultListableBeanFactory的preInstantiateSingletons方法

public void preInstantiateSingletons() throws BeansException {        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);    // Trigger initialization of all non-lazy singleton beans...    for (String beanName : beanNames) {      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 判斷為非抽象類、是單例、非懶加載 才給初始化        if (isFactoryBean(beanName)) {          // 無關(guān)代碼(針對FactoryBean的處理)        }        else {          // 重要!?。∑胀╞ean就是在這里初始化的          getBean(beanName);        }      }    }    // 其他無關(guān)代碼   }

可以看到,就是在此方法中循環(huán)Spring容器中所有的bean,依次對其進(jìn)行初始化,初始化的入口就是getBean方法

3、AbstractBeanFactory的getBean跟doGetBean方法

追蹤getBean方法:

public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }

可見引用了重載的doGetBean方法,繼續(xù)追蹤之:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {    final String beanName = transformedBeanName(name);    Object bean;             // 方法1)從三個map中獲取單例類    Object sharedInstance = getSingleton(beanName);    // 省略無關(guān)代碼    }    else {      // 如果是多例的循環(huán)引用,則直接報錯      if (isPrototypeCurrentlyInCreation(beanName)) {        throw new BeanCurrentlyInCreationException(beanName);      }      // 省略若干無關(guān)代碼      try {        // Create bean instance.        if (mbd.isSingleton()) {          // 方法2) 獲取單例對象          sharedInstance = getSingleton(beanName, () -> {            try { //方法3) 創(chuàng)建ObjectFactory中g(shù)etObject方法的返回值              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);        }     }    // 省略若干無關(guān)代碼    return (T) bean;  }

該方法比較長,對于解決循環(huán)引用來說,上面標(biāo)出來的3個方法起到了至關(guān)重要的作用,下面我們挨個攻克。

3.1) getSingleton(beanName)方法: 注意該方法跟方法2)是重載方法,名字一樣內(nèi)部邏輯卻大相徑庭。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {    Object singletonObject = this.singletonObjects.get(beanName);// 步驟A    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {      synchronized (this.singletonObjects) {        singletonObject = this.earlySingletonObjects.get(beanName);// 步驟B        if (singletonObject == null && allowEarlyReference) {          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);// 步驟C          if (singletonFactory != null) {            singletonObject = singletonFactory.getObject();            this.earlySingletonObjects.put(beanName, singletonObject);            this.singletonFactories.remove(beanName);          }        }      }    }    return singletonObject;  }

通過上面的步驟可以看出這三個map的優(yōu)先級。其中singletonObjects里面存放的是初始化之后的單例對象;earlySingletonObjects中存放的是一個已完成實例化未完成初始化的早期單例對象;而singletonFactories中存放的是ObjectFactory對象,此對象的getObject方法返回值即剛完成實例化還未開始初始化的單例對象。所以先后順序是,單例對象先存在于singletonFactories中,后存在于earlySingletonObjects中,最后初始化完成后放入singletonObjects中。

當(dāng)debug到此處時,以上述Teacher和Student兩個循環(huán)引用的類為例,如果第一個走到這一步的是Teacher,則從此處這三個map中g(shù)et到的值都是空,因為還未添加進(jìn)去。這個方法主要是給循環(huán)依賴中后來過來的對象用。

3.2)getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法:

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) {        // 省略無關(guān)代碼        beforeSingletonCreation(beanName); // 步驟A        boolean newSingleton = false;        // 省略無關(guān)代碼        try {          singletonObject = singletonFactory.getObject();// 步驟B          newSingleton = true;        }        // 省略無關(guān)代碼        finally {          if (recordSuppressedExceptions) {            this.suppressedExceptions = null;          }          afterSingletonCreation(beanName);// 步驟C        }        if (newSingleton) {          addSingleton(beanName, singletonObject);// 步驟D        }      }      return singletonObject;    }  }

獲取單例對象的主要邏輯就是此方法實現(xiàn)的,主要分為上面四個步驟,繼續(xù)挨個看:

步驟A:

protected void beforeSingletonCreation(String beanName) { // 判斷,并首次將beanName即teacher放入singletonsCurrentlyInCreation中 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }

步驟C:

protected void afterSingletonCreation(String beanName) { // 得到單例對象后,再講beanName從singletonsCurrentlyInCreation中移除 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } }

步驟D:

protected void addSingleton(String beanName, Object singletonObject) {    synchronized (this.singletonObjects) {      this.singletonObjects.put(beanName, singletonObject);//添加單例對象到map中      this.singletonFactories.remove(beanName);//從早期暴露的工廠中移除,此map在解決循環(huán)依賴中發(fā)揮了關(guān)鍵的作用      this.earlySingletonObjects.remove(beanName);//從早期暴露的對象map中移除      this.registeredSingletons.add(beanName);//添加到已注冊的單例名字集合中    }  }

步驟B:

此處調(diào)用了ObjectFactory的getObject方法,此方法是在哪里實現(xiàn)的呢?返回的又是什么?且往回翻,找到3中的方法3,對java8函數(shù)式編程有過了解的園友應(yīng)該能看出來,方法3 【createBean(beanName, mbd, args)】的返回值就是getObject方法的返回值,即方法3返回的就是我們需要的單例對象,下面且追蹤方法3而去。

3.3)AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[]) 方法

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)      throws BeanCreationException {    // 省略無關(guān)代碼    try {      Object beanInstance = doCreateBean(beanName, mbdToUse, args);      return beanInstance;    }    // 省略無關(guān)代碼  }

去掉無關(guān)代碼之后,關(guān)鍵方法只有doCreateBean方法,追蹤之:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)      throws BeanCreationException {    BeanWrapper instanceWrapper = null;    // 省略代碼    if (instanceWrapper == null) {      // 實例化bean      instanceWrapper = createBeanInstance(beanName, mbd, args);    }    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&        isSingletonCurrentlyInCreation(beanName));    if (earlySingletonExposure) {      // 重點?。?!將實例化的對象添加到singletonFactories中       addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));    }    // 初始化bean    Object exposedObject = bean;    try {      populateBean(beanName, mbd, instanceWrapper);//也很重要      exposedObject = initializeBean(beanName, exposedObject, mbd);    }    // 省略無關(guān)代碼    return exposedObject;}

上面注釋中標(biāo)出的重點是此方法的關(guān)鍵。在addSingletonFactory方法中,將第二個參數(shù)ObjectFactory存入了singletonFactories供其他對象依賴時調(diào)用。然后下面的populateBean方法對剛實例化的bean進(jìn)行屬性注入(該方法關(guān)聯(lián)較多,本文暫時不展開追蹤了,有興趣的園友自行查看即可),如果遇到Spring中的對象屬性,則再通過getBean方法獲取該對象。至此,循環(huán)依賴在Spring中的處理過程已經(jīng)追溯完畢,下面我們總結(jié)一下。

小結(jié)

屬性注入主要是在populateBean方法中進(jìn)行的。對于循環(huán)依賴,以我們上文中的Teacher中注入了Student、Student中注入了Teacher為例來說明,假定Spring的加載順序為先加載Teacher,再加載Student。

getBean方法觸發(fā)Teacher的初始化后:

a. 首先走到3中的方法1),此時map中都為空,獲取不到實例;

b. 然后走到方法2)中,步驟A、步驟C、步驟D為控制map中數(shù)據(jù)的方法,實現(xiàn)簡單,可暫不關(guān)注。其中步驟B的getObject方法觸發(fā)對方法3)的調(diào)用;

c. 在方法3)中,先通過createBeanInstance實例化Teacher對象,又將該實例化的對象通過addSingletonFactory方法放入singletonFactories中,完成Teacher對象早期的暴露;

d. 然后在方法3)中通過populateBean方法對Teacher對象進(jìn)行屬性的注入,發(fā)現(xiàn)它有一個Student屬性,則觸發(fā)getBean方法對Student進(jìn)行初始化

e. 重復(fù)a、b、c步驟,只是此時要初始化的是Student對象

f. 走到d的時候,調(diào)用populateBean對Student對象進(jìn)行屬性注入,發(fā)現(xiàn)它有一個Teacher屬性,則觸發(fā)getBean方法對Teacher進(jìn)行初始化;

g. 對Teacher進(jìn)行初始化,又來到a,但此時map已經(jīng)不為空了,因為之前在c步驟中已經(jīng)將Teacher實例放入了singletonFactories中,a中得到Teacher實例后返回;

h.完成f中對Student的初始化,繼而依次往上回溯完成Teacher的初始化;

完成Teacher的初始化后,Student的初始化就簡單了,因為map中已經(jīng)存了這個單例。

至此,Spring循環(huán)依賴的總結(jié)分析結(jié)束,一句話來概括一下:Spring通過將實例化后的對象提前暴露給Spring容器中的singletonFactories,解決了循環(huán)依賴的問題。

到此,關(guān)于“Spring中循環(huán)依賴的解決方法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

新聞標(biāo)題:Spring中循環(huán)依賴的解決方法
分享路徑:http://aaarwkj.com/article18/jespdp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機網(wǎng)站建設(shè)、網(wǎng)站制作搜索引擎優(yōu)化、品牌網(wǎng)站設(shè)計電子商務(wù)、域名注冊

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名
国产一区二区三区婷婷| 天美传媒剧国产在线观看| 一区二区三区一级黄色| 午夜一区二区精品视频国产| 亚洲欧美精品一中文字幕| 国产中文字幕精品在线观看| 丁香六月色婷婷亚洲激情| 成年女人大片免费观看版| 欧美一级特黄大片做受另类| 欧美日韩国产看片一区二区| 桃色av一区二区三区| 在线欧美亚洲观看天堂| 亚洲一区二区日本久久| 日韩高清有码一区二区| 不卡视频一区二区日韩| 亚洲少妇精品视频在线| 成人在线视频国产自拍| 日本高清一区二区网站| 又黄又湿又刺激中文字幕| 日韩欧美亚洲天堂视频| 亚洲一区二区三区av电影| 国产精品成久久久久三级| 在线看电影亚洲一区| 日韩av在线观看大全| 亚洲一区在线观看蜜臀| 亚洲不卡高清一区二区三区| 国产成人+亚洲欧洲综合| 色哟哟精品丝袜一区二区| 99在线视频午夜福利| 亚洲欧美激情啪啪啪| 白虎亚洲福利精品一区| 亚洲日本高清一二三区| 国产亚洲精品第一最新| 国产成人亚洲欧美激情| 国产免费看黄色的网站| 未满十八禁止下载软件| 日韩色图在线观看视频| 国产精品一久久香蕉产线看 | 日本熟女视频中文字幕| 亚洲一区二区三区无人区| 欧美日韩三级国产在线|