• spring如何解决循环依赖


    什么是循环依赖
    循环依赖其实是指两个及以上bean相互持有对方,最终形成闭环的过程(一般聊循环依赖都是默认的单例bean),简单说就是A依赖B,B依赖C,C又依赖A。

    下面我就借用别人的网图来解释下:

    注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。

    Spring中循环依赖场景主要有以下两种:
    (1)field属性的循环依赖
    (2)构造器的循环依赖
    (3)DependsOn循环依赖

    2、怎么检测循环依赖
    检测循环依赖相对比较容易,Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。

    3、Spring怎么解决循环依赖

    Spring解决循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field或则属性是可以延后设置的(但是构造器必须是在获取引用之前)。

    Spring的单例对象的初始化主要分为三步:

    实例化:其实也就是调用对象的构造方法实例化对象
    注入:填充属性,这一步主要是对bean的依赖属性进行填充
    初始化:属性注入后,执行自定义初始化
     

     

     

     

    其实在Spring中,有两种循环依赖的场景...


    第一种:构造器的循环依赖
    第二种:setter的依赖注入

    第一种是没有办法解决的,而第二种可以使用提前暴露对象的方式进行解决

    那么肯定有很多小伙伴会有疑问,为什么?
    其实想搞明白这个问题,需要对Spring的Bean的生命周期有一个完整的了解,如下图:

     

    大家可能对这个图不太熟悉,但是我要强调的是,图中我已经用三种颜色标识了,除了使用这部分之外,可以分为实例化和初始化,这也是解决问题的核心。
    同时,Spring中还使用了三级缓存来解决问题: 

    spring获取单例源码可见

     什么是三级缓存就是上面的3个map

    获取单例

    1. public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    2. Assert.notNull(beanName, "Bean name must not be null");
    3. synchronized(this.singletonObjects) {
    4. Object singletonObject = this.singletonObjects.get(beanName);
    5. if (singletonObject == null) {
    6. if (this.singletonsCurrentlyInDestruction) {
    7. 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!)");
    8. }
    9. if (this.logger.isDebugEnabled()) {
    10. this.logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
    11. }
    12. this.beforeSingletonCreation(beanName);
    13. boolean newSingleton = false;
    14. boolean recordSuppressedExceptions = this.suppressedExceptions == null;
    15. if (recordSuppressedExceptions) {
    16. this.suppressedExceptions = new LinkedHashSet();
    17. }
    18. try {
    19. singletonObject = singletonFactory.getObject();
    20. newSingleton = true;
    21. } catch (IllegalStateException var16) {
    22. singletonObject = this.singletonObjects.get(beanName);
    23. if (singletonObject == null) {
    24. throw var16;
    25. }
    26. } catch (BeanCreationException var17) {
    27. BeanCreationException ex = var17;
    28. if (recordSuppressedExceptions) {
    29. Iterator var8 = this.suppressedExceptions.iterator();
    30. while(var8.hasNext()) {
    31. Exception suppressedException = (Exception)var8.next();
    32. ex.addRelatedCause(suppressedException);
    33. }
    34. }
    35. throw ex;
    36. } finally {
    37. if (recordSuppressedExceptions) {
    38. this.suppressedExceptions = null;
    39. }
    40. this.afterSingletonCreation(beanName);
    41. }
    42. if (newSingleton) {
    43. this.addSingleton(beanName, singletonObject);
    44. }
    45. }
    46. return singletonObject;
    47. }
    48. }

    上面这段代码就是先从1级缓存取,取到就返回,取不到就

    this.beforeSingletonCreation(beanName);
    1. protected void beforeSingletonCreation(String beanName) {
    2. if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
    3. throw new BeanCurrentlyInCreationException(beanName);
    4. }
    5. }
    inCreationCheckExclusions集合不包含这个bean就把bean放入singletonsCurrentlyInCreation

     finally执行

    1. protected void afterSingletonCreation(String beanName) {
    2. if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
    3. throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    4. }
    5. }

    inCreationCheckExclusions不包含bean就把bean从singletonsCurrentlyInCreation删除

    添加单例工厂

    1. protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
    2. Assert.notNull(singletonFactory, "Singleton factory must not be null");
    3. synchronized(this.singletonObjects) {
    4. if (!this.singletonObjects.containsKey(beanName)) {
    5. this.singletonFactories.put(beanName, singletonFactory);
    6. this.earlySingletonObjects.remove(beanName);
    7. this.registeredSingletons.add(beanName);
    8. }
    9. }
    10. }

    下面解读下下面的这段代码

     先解释下两个参数:

    allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象
    isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中,也就是没有初始化完成(比如A的构造器依赖了B对象所以得先去创建B对象, 或则在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态。)


    分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则:
    从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。
     

    1.  this.earlySingletonObjects.put(beanName, singletonObject);
    2.     // 从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。
    3.     this.singletonFactories.remove(beanName);

    从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory,定义如下:

    1. @FunctionalInterface
    2. public interface ObjectFactory<T> {
    3.     /**
    4.      * Return an instance (possibly shared or independent)
    5.      * of the object managed by this factory.
    6.      * @return the resulting instance
    7.      * @throws BeansException in case of creation errors
    8.      */
    9.     T getObject() throws BeansException;
    10. }

    1. 调用createBeanInstance实例化后,如果bean是单例,且允许从singletonFactories获取bean,并且当前bean正在创建中,那么就把beanName放入三级缓存(singletonFactories)中:
    2. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
    1. protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    2. if (this.logger.isTraceEnabled()) {
    3. this.logger.trace("Creating instance of bean '" + beanName + "'");
    4. }
    5. RootBeanDefinition mbdToUse = mbd;
    6. Class<?> resolvedClass = this.resolveBeanClass(mbd, beanName, new Class[0]);
    7. if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
    8. mbdToUse = new RootBeanDefinition(mbd);
    9. mbdToUse.setBeanClass(resolvedClass);
    10. }
    11. try {
    12. mbdToUse.prepareMethodOverrides();
    13. } catch (BeanDefinitionValidationException var9) {
    14. throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", var9);
    15. }
    16. Object beanInstance;
    17. try {
    18. beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse);
    19. if (beanInstance != null) {
    20. return beanInstance;
    21. }
    22. } catch (Throwable var10) {
    23. throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", var10);
    24. }
    25. try {
    26. beanInstance = this.doCreateBean(beanName, mbdToUse, args);
    27. if (this.logger.isTraceEnabled()) {
    28. this.logger.trace("Finished creating instance of bean '" + beanName + "'");
    29. }
    30. return beanInstance;
    31. } catch (ImplicitlyAppearedSingletonException | BeanCreationException var7) {
    32. throw var7;
    33. } catch (Throwable var8) {
    34. throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", var8);
    35. }
    36. }

    调用createBeanInstance实例化后,如果bean是单例,且允许从singletonFactories获取bean,并且当前bean正在创建中,那么就把beanName放入三级缓存(singletonFactories)中:
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

    1. protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    2. BeanWrapper instanceWrapper = null;
    3. if (mbd.isSingleton()) {
    4. instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
    5. }
    6. if (instanceWrapper == null) {
    7. instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    8. }
    9. Object bean = instanceWrapper.getWrappedInstance();
    10. Class<?> beanType = instanceWrapper.getWrappedClass();
    11. if (beanType != NullBean.class) {
    12. mbd.resolvedTargetType = beanType;
    13. }
    14. synchronized(mbd.postProcessingLock) {
    15. if (!mbd.postProcessed) {
    16. try {
    17. this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    18. } catch (Throwable var17) {
    19. throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17);
    20. }
    21. mbd.postProcessed = true;
    22. }
    23. }
    24. boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
    25. if (earlySingletonExposure) {
    26. if (this.logger.isTraceEnabled()) {
    27. this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
    28. }
    29. this.addSingletonFactory(beanName, () -> {
    30. return this.getEarlyBeanReference(beanName, mbd, bean);
    31. });
    32. }
    33. Object exposedObject = bean;
    34. try {
    35. this.populateBean(beanName, mbd, instanceWrapper);
    36. exposedObject = this.initializeBean(beanName, exposedObject, mbd);
    37. } catch (Throwable var18) {
    38. if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
    39. throw (BeanCreationException)var18;
    40. }
    41. throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
    42. }
    43. if (earlySingletonExposure) {
    44. Object earlySingletonReference = this.getSingleton(beanName, false);
    45. if (earlySingletonReference != null) {
    46. if (exposedObject == bean) {
    47. exposedObject = earlySingletonReference;
    48. } else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
    49. String[] dependentBeans = this.getDependentBeans(beanName);
    50. Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
    51. String[] var12 = dependentBeans;
    52. int var13 = dependentBeans.length;
    53. for(int var14 = 0; var14 < var13; ++var14) {
    54. String dependentBean = var12[var14];
    55. if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
    56. actualDependentBeans.add(dependentBean);
    57. }
    58. }
    59. if (!actualDependentBeans.isEmpty()) {
    60. 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.");
    61. }
    62. }
    63. }
    64. }
    65. try {
    66. this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
    67. return exposedObject;
    68. } catch (BeanDefinitionValidationException var16) {
    69. throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
    70. }
    71. }

     

     

    1. protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
    2. Assert.notNull(singletonFactory, "Singleton factory must not be null");
    3. synchronized(this.singletonObjects) {
    4. if (!this.singletonObjects.containsKey(beanName)) {
    5. this.singletonFactories.put(beanName, singletonFactory);
    6. this.earlySingletonObjects.remove(beanName);
    7. this.registeredSingletons.add(beanName);
    8. }
    9. }
    10. }
    11. 一级缓存没有这个bean,就把bean放入三级缓存,并且删除二级缓存,并且把bean注册到registeredSingletons

     

    这里就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。
     

  • 相关阅读:
    让AI保持怪异
    11.QA模型选择,欠拟合和过拟合
    BIOMOD2模型、MaxEnt模型物种分布模拟,生物多样性生境模拟,论文写作
    【Vue2 全局前置导航守卫】
    FFmpeg+javacpp+javacv使用
    elf 文件信息的用途
    Springboot项目log4j与logback的Jar包冲突问题
    优化|优化求解器自动调参
    编码器如何控制单相霍尔电机。只有一路霍尔信号,电机只能正转不能反转。能移植野火Pid控制吗
    k8s svc流量转发
  • 原文地址:https://blog.csdn.net/ywl470812087/article/details/127688060