成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

一起來讀Spring源碼吧(四)循環(huán)依賴踩坑筆記

jas0n / 1726人閱讀

摘要:從創(chuàng)建的的起點(diǎn)開始首先會(huì)在緩存中查找,這里一共有三級(jí)緩存,保存初始化完成的單例實(shí)例,保存提前曝光的單例實(shí)例,保存單例的工廠函數(shù)對(duì)象后面兩級(jí)都是為了解決循環(huán)依賴設(shè)置的,具體查找邏輯在后續(xù)其他情況下調(diào)用會(huì)說明。

源起

在開發(fā)過程中,遇到需要把方法調(diào)用改為異步的情況,本來以為簡(jiǎn)單得加個(gè)@Asyn在方法上就行了,沒想到項(xiàng)目啟動(dòng)的時(shí)候報(bào)了如下的錯(cuò)誤:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name "customerServiceImpl": 
Bean with name "customerServiceImpl" has been injected into other beans [customerServiceImpl,followServiceImpl,cupidService] in its raw version as part of a circular reference, 
but has eventually been wrapped. This means that said other beans do not use the final version of the bean. 
This is often the result of over-eager type matching - consider using "getBeanNamesOfType" with the "allowEagerInit" flag turned off, for example.

看了下好像報(bào)的是循環(huán)依賴的錯(cuò)誤,但是Spring單例是支持循環(huán)依賴的,當(dāng)時(shí)一臉懵逼。
拿著報(bào)錯(cuò)去百度了下,說是多個(gè)動(dòng)態(tài)代理導(dǎo)致的循環(huán)依賴報(bào)錯(cuò),也找到了報(bào)錯(cuò)的地點(diǎn),但是還是不明白為什么會(huì)這樣,所以打算深入源碼探個(gè)究竟,順便回顧下Bean的獲取流程和循環(huán)依賴的內(nèi)容。

模擬場(chǎng)景

用SpringBoot新建一個(gè)demo項(xiàng)目,因?yàn)樵?xiàng)目是有定義切面的,這里也定義一個(gè)切面:

@Aspect
@Component
public class TestAspect {

    @Pointcut("execution(public * com.example.demo.service.CyclicDependencyService.sameClassMethod(..))")
    private void testPointcut() {}

    @AfterReturning("testPointcut()")
    public void after(JoinPoint point) {
        System.out.println("在" + point.getSignature() + "之后干點(diǎn)事情");
    }

}

然后新建一個(gè)注入自己的Service構(gòu)成循環(huán)依賴,然后提供一個(gè)方法滿足切點(diǎn)要求,并且加上@Async注解:

@Service
public class CyclicDependencyService {

    @Autowired
    private CyclicDependencyService cyclicDependencyService;

    public void test() {
        System.out.println("調(diào)用同類方法");
        cyclicDependencyService.sameClassMethod();
    }

    @Async
    public void sameClassMethod() {
        System.out.println("循環(huán)依賴中的異步方法");
        System.out.println("方法線程:" + Thread.currentThread().getName());
    }

}

還有別忘了給Application啟動(dòng)類加上@EnableAsync和@EnableAspectJAutoProxy:

@EnableAsync
@EnableAspectJAutoProxy
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

最后打好斷點(diǎn),開始debug。

debug

從Bean創(chuàng)建的的起點(diǎn)--AbstractBeanFactory#getBean開始

// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);

首先會(huì)在緩存中查找,DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference):

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) {
            ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

這里一共有三級(jí)緩存:

singletonObjects,保存初始化完成的單例bean實(shí)例;

earlySingletonObjects,保存提前曝光的單例bean實(shí)例;

singletonFactories,保存單例bean的工廠函數(shù)對(duì)象;

后面兩級(jí)都是為了解決循環(huán)依賴設(shè)置的,具體查找邏輯在后續(xù)其他情況下調(diào)用會(huì)說明。

緩存中找不到,就要?jiǎng)?chuàng)建單例:

sharedInstance = getSingleton(beanName, () -> {
   try {
      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;
   }
});

調(diào)用DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory singletonFactory):

public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
    ...
    beforeSingletonCreation(beanName);
    ...
    singletonObject = singletonFactory.getObject();
    ...
    afterSingletonCreation(beanName);
    ...
    addSingleton(beanName, singletonObject);
    ...
}

創(chuàng)建前后分別做了這幾件事:

前,beanName放入singletonsCurrentlyInCreation,表示單例正在創(chuàng)建中

后,從singletonsCurrentlyInCreation中移除beanName

后,將創(chuàng)建好的bean放入singletonObjects,移除在singletonFactories和earlySingletonObjects的對(duì)象

創(chuàng)建單例調(diào)用getSingleton時(shí)傳入的工廠函數(shù)對(duì)象的getObject方法,實(shí)際上就是createBean方法,主要邏輯在AbstractAutowireCapableBeanFactory#doCreateBean中:

...
instanceWrapper = createBeanInstance(beanName, mbd, args);
final Object bean = instanceWrapper.getWrappedInstance();
...
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
      logger.trace("Eagerly caching bean "" + beanName +
            "" to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
   populateBean(beanName, mbd, instanceWrapper);
   exposedObject = initializeBean(beanName, exposedObject, mbd);
}
...
if (earlySingletonExposure) {
   Object earlySingletonReference = getSingleton(beanName, false);
   if (earlySingletonReference != null) {
      if (exposedObject == bean) {
         exposedObject = earlySingletonReference;
      }
      else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
         String[] dependentBeans = getDependentBeans(beanName);
         Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
         for (String dependentBean : dependentBeans) {
            if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
               actualDependentBeans.add(dependentBean);
            }
         }
         if (!actualDependentBeans.isEmpty()) {
            throw new BeanCurrentlyInCreationException(beanName,
                  "Bean with name "" + beanName + "" has been injected into other beans [" +
                  StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                  "] in its raw version as part of a circular reference, but has eventually been " +
                  "wrapped. This means that said other beans do not use the final version of the " +
                  "bean. This is often the result of over-eager type matching - consider using " +
                  ""getBeanNamesOfType" with the "allowEagerInit" flag turned off, for example.");
         }
      }
   }
}

可以看到報(bào)錯(cuò)就是在這個(gè)方法里拋出的,那么這個(gè)方法就是重點(diǎn)中的重點(diǎn)。

首先實(shí)例化單例,instantiate,只是實(shí)例化獲取對(duì)象引用,還沒有注入依賴。我debug時(shí)記錄的bean對(duì)象是CyclicDependencyService@4509;

然后判斷bean是否需要提前暴露,需要滿足三個(gè)條件:1、是單例;2、支持循環(huán)依賴;3、bean正在創(chuàng)建中,也就是到前面提到的singletonsCurrentlyInCreation中能查找到,全滿足的話就會(huì)調(diào)用DefaultSingletonBeanRegistry#addSingletonFactory把beanName和單例工廠函數(shù)對(duì)象(匿名實(shí)現(xiàn)調(diào)用AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法)放入singletonFactories;

接著就是注入依賴,填充屬性,具體怎么注入這里就不展開了,最后會(huì)為屬性cyclicDependencyService調(diào)用DefaultSingletonBeanRegistry.getSingleton(beanName, true),注意這里和最開始的那次調(diào)用不一樣,isSingletonCurrentlyInCreation為true,就會(huì)在singletonFactories中找到bean的單例工廠函數(shù)對(duì)象,也就是在上一步提前暴露時(shí)放入的,然后調(diào)用它的匿名實(shí)現(xiàn)AbstractAutowireCapableBeanFactory#getEarlyBeanReference:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
         }
      }
   }
   return exposedObject;
}

方法邏輯就是挨個(gè)調(diào)用實(shí)現(xiàn)了SmartInstantiationAwareBeanPostProcessor接口的后置處理器(以下簡(jiǎn)稱BBP)的getEarlyBeanReference方法。一個(gè)一個(gè)debug下來,其他都是原樣返回bean,只有AnnotationAwareAspectJAutoProxyCreator會(huì)把原bean(CyclicDependencyService@4509)存在earlyProxyReferences,然后將bean的代理返回(debug時(shí)記錄的返回對(duì)象是CyclicDependencyService$$EnhancerBySpringCGLIB$$6ed9e2db@4740)并放入earlySingletonObjects,再賦給屬性cyclicDependencyService。

public Object getEarlyBeanReference(Object bean, String beanName) {
   Object cacheKey = getCacheKey(bean.getClass(), beanName);
   this.earlyProxyReferences.put(cacheKey, bean);
   return wrapIfNecessary(bean, beanName, cacheKey);
}

屬性填充完成后就是調(diào)用初始化方法AbstractAutowireCapableBeanFactory#initializeBean:

...
invokeAwareMethods(beanName, bean);
...
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
...
invokeInitMethods(beanName, wrappedBean, mbd);
...
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
...

初始化主要分為這幾步:

如果bean實(shí)現(xiàn)了BeanNameAware、BeanClassLoaderAware或BeanFactoryAware,把相應(yīng)的資源放入bean;

順序執(zhí)行BBP的postProcessBeforeInitialization方法;

如果實(shí)現(xiàn)了InitializingBean就執(zhí)行afterPropertiesSet方法,然后執(zhí)行自己的init-method;

順序執(zhí)行BBP的postProcessAfterInitialization。

debug的時(shí)候發(fā)現(xiàn)是第4步改變了bean,先執(zhí)行AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

這里會(huì)獲取并移除之前存在earlyProxyReferences的bean(CyclicDependencyService@4509),因?yàn)楹彤?dāng)前bean是同一個(gè)對(duì)象,所以什么都沒做直接返回。隨后會(huì)執(zhí)行AsyncAnnotationBeanPostProcessor#postProcessAfterInitialization:

if (isEligible(bean, beanName)) {
   ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
   if (!proxyFactory.isProxyTargetClass()) {
      evaluateProxyInterfaces(bean.getClass(), proxyFactory);
   }
   proxyFactory.addAdvisor(this.advisor);
   customizeProxyFactory(proxyFactory);
   return proxyFactory.getProxy(getProxyClassLoader());
}

先判斷bean是否有需要代理,因?yàn)镃yclicDependencyService有方法帶有@Async注解就需要代理,返回代理對(duì)象是CyclicDependencyService$$EnhancerBySpringCGLIB$$e66d8f6e@5273

返回的代理對(duì)象賦值給AbstractAutowireCapableBeanFactory#doCreateBean方法內(nèi)的exposedObject,接下來就到了檢查循環(huán)依賴的地方了:

if (earlySingletonExposure) {
   Object earlySingletonReference = getSingleton(beanName, false);
   if (earlySingletonReference != null) {
      if (exposedObject == bean) {
         exposedObject = earlySingletonReference;
      }
      else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
         String[] dependentBeans = getDependentBeans(beanName);
         Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
         for (String dependentBean : dependentBeans) {
            if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
               actualDependentBeans.add(dependentBean);
            }
         }
         if (!actualDependentBeans.isEmpty()) {
            throw new BeanCurrentlyInCreationException(beanName,
                  "Bean with name "" + beanName + "" has been injected into other beans [" +
                  StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                  "] in its raw version as part of a circular reference, but has eventually been " +
                  "wrapped. This means that said other beans do not use the final version of the " +
                  "bean. This is often the result of over-eager type matching - consider using " +
                  ""getBeanNamesOfType" with the "allowEagerInit" flag turned off, for example.");
         }
      }
   }
}

首先從earlySingletonObjects里拿到前面屬性填充時(shí)放入的bean代理(CyclicDependencyService$$EnhancerBySpringCGLIB$$6ed9e2db@4740),不為空的話就比較bean和exposedObject,分別是CyclicDependencyService@4509CyclicDependencyService$$EnhancerBySpringCGLIB$$e66d8f6e@5273,很明顯不是同一個(gè)對(duì)象,然后會(huì)判斷allowRawInjectionDespiteWrapping屬性和是否有依賴的bean,然后判斷這些bean是否是真實(shí)依賴的,一旦存在真實(shí)依賴的bean,就會(huì)拋出BeanCurrentlyInCreationException。

總結(jié)

總結(jié)下Spring解決循環(huán)依賴的思路:在創(chuàng)建bean時(shí),對(duì)于滿足提前曝光條件的單例,會(huì)把該單例的工廠函數(shù)對(duì)象放入三級(jí)緩存中的singletonFactories中;然后在填充屬性時(shí),如果存在循環(huán)依賴,必然會(huì)嘗試獲取該單例,也就是執(zhí)行之前放入的工廠函數(shù)的匿名實(shí)現(xiàn),這時(shí)候拿到的有可能是原bean對(duì)象,也有可能是被某些BBP處理過返回的代理對(duì)象,會(huì)放入三級(jí)緩存中的earlySingletonObjects中;接著bean開始初始化,結(jié)果返回的有可能是原bean對(duì)象,也有可能是代理對(duì)象;最后對(duì)于滿足提前曝光的單例,如果真的有提前曝光的動(dòng)作,就會(huì)去檢查初始化后的bean對(duì)象是不是原bean對(duì)象是同一個(gè)對(duì)象,只有不是的情況下才可能拋出異常。重點(diǎn)就在于存在循環(huán)依賴的情況下,初始化過的bean對(duì)象是不是跟原bean是同一個(gè)對(duì)象。

從以上的debug過程可以看出,是AsyncAnnotationBeanPostProcessor這個(gè)BBP在初始化過程中改變了bean,使得結(jié)果bean和原bean不是一個(gè)對(duì)象,而AnnotationAwareAspectJAutoProxyCreator則是在填充屬性獲取提前曝光的對(duì)象時(shí)把原始bean緩存起來,返回代理的bean。然后在初始化時(shí)執(zhí)行它的postProcessAfterInitialization方法時(shí)如果傳入的bean是之前緩存的原始bean,就直接返回,不進(jìn)行代理。如果其他BBP也都沒有改變bean的話,初始化過后的bean就是跟原始bean是同一個(gè)對(duì)象,這時(shí)就會(huì)把提前曝光的對(duì)象(代理過的)作為最終生成的bean。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77558.html

相關(guān)文章

  • Spring IOC 容器源碼分析 - 循環(huán)依賴的解決辦法

    摘要:實(shí)例化時(shí),發(fā)現(xiàn)又依賴于。一些緩存的介紹在進(jìn)行源碼分析前,我們先來看一組緩存的定義??墒强赐暝创a后,我們似乎仍然不知道這些源碼是如何解決循環(huán)依賴問題的。 1. 簡(jiǎn)介 本文,我們來看一下 Spring 是如何解決循環(huán)依賴問題的。在本篇文章中,我會(huì)首先向大家介紹一下什么是循環(huán)依賴。然后,進(jìn)入源碼分析階段。為了更好的說明 Spring 解決循環(huán)依賴的辦法,我將會(huì)從獲取 bean 的方法getB...

    aikin 評(píng)論0 收藏0
  • 【轉(zhuǎn)】成為Java頂尖程序員 ,看這10本書就夠了

    摘要:實(shí)戰(zhàn)高并發(fā)程序設(shè)計(jì)這本書是目前點(diǎn)評(píng)推薦比較多的書,其特色是案例小,好實(shí)踐代碼有場(chǎng)景,實(shí)用。想要學(xué)習(xí)多線程的朋友,這本書是我大力推薦的,我的個(gè)人博客里面二十多篇的多線程博文都是基于此書,并且在這本書的基礎(chǔ)上進(jìn)行提煉和總結(jié)而寫出來的。 學(xué)習(xí)的最好途徑就是看書,這是我自己學(xué)習(xí)并且小有了一定的積累之后的第一體會(huì)。個(gè)人認(rèn)為看書有兩點(diǎn)好處:showImg(/img/bVr5S5);  1.能出版出...

    DTeam 評(píng)論0 收藏0
  • Java進(jìn)階之路

    摘要:探索專為而設(shè)計(jì)的將探討進(jìn)行了何種改進(jìn),以及這些改進(jìn)背后的原因。關(guān)于最友好的文章進(jìn)階前言之前就寫過一篇關(guān)于最友好的文章反響很不錯(cuò),由于那篇文章的定位就是簡(jiǎn)單友好,因此盡可能的摒棄復(fù)雜的概念,只抓住關(guān)鍵的東西來講,以保證大家都能看懂。 周月切換日歷 一個(gè)可以進(jìn)行周月切換的日歷,左右滑動(dòng)的切換月份,上下滑動(dòng)可以進(jìn)行周,月不同的視圖切換,可以進(jìn)行事件的標(biāo)記,以及節(jié)假日的顯示,功能豐富 Andr...

    sushi 評(píng)論0 收藏0
  • Spring IOC 容器源碼分析系列文章導(dǎo)

    摘要:本文是容器源碼分析系列文章的第一篇文章,將會(huì)著重介紹的一些使用方法和特性,為后續(xù)的源碼分析文章做鋪墊。我們可以通過這兩個(gè)別名獲取到這個(gè)實(shí)例,比如下面的測(cè)試代碼測(cè)試結(jié)果如下本小節(jié),我們來了解一下這個(gè)特性。 1. 簡(jiǎn)介 Spring 是一個(gè)輕量級(jí)的企業(yè)級(jí)應(yīng)用開發(fā)框架,于 2004 年由 Rod Johnson 發(fā)布了 1.0 版本。經(jīng)過十幾年的迭代,現(xiàn)在的 Spring 框架已經(jīng)非常成熟了...

    NSFish 評(píng)論0 收藏0
  • Spring Security

    摘要:框架具有輕便,開源的優(yōu)點(diǎn),所以本譯見構(gòu)建用戶管理微服務(wù)五使用令牌和來實(shí)現(xiàn)身份驗(yàn)證往期譯見系列文章在賬號(hào)分享中持續(xù)連載,敬請(qǐng)查看在往期譯見系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪問層和前端控制器但是忽略了對(duì)身份進(jìn)行驗(yàn)證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護(hù)REST API 重拾后端之Spring Boot(一):REST API的搭建...

    keelii 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<