Spring碎片整理-创建Bean对象时循环依赖的解决

原文链接: http://www.cnblogs.com/ZzlevolFlash/p/10879841.html

写在前面

我们来看一下这样的二个对象:

  public class CycleReferenceBeanA {
      private CycleReferenceBeanB cycleReferenceBeanB;

      public CycleReferenceBeanB getCycleReferenceBeanB() {
          return cycleReferenceBeanB;
      }

      public void setCycleReferenceBeanB(CycleReferenceBeanB cycleReferenceBeanB) {
          this.cycleReferenceBeanB = cycleReferenceBeanB;
      }
  }

  public class CycleReferenceBeanB {
      private CycleReferenceBeanA cycleReferenceBeanA;

      public CycleReferenceBeanA getCycleReferenceBeanA() {
          return cycleReferenceBeanA;
      }

      public void setCycleReferenceBeanA(CycleReferenceBeanA cycleReferenceBeanA) {
          this.cycleReferenceBeanA = cycleReferenceBeanA;
      }
  }

如果你没有学习过Spring的源码,那你很可能注意不到这一点。如果一个对象A中有B的依赖,而同时B又有A的依赖时,Spring是如何处理这种情况的呢?每个人在阅读Spring源码时都绕不过循环依赖这个关键字,接下来我们就一起看一看吧。(本文主要来自书《Spring源码深度解析》中的一个案例)


在看具体的实例之前,我们先了解一个概念:

Spring容器在创建对象的过程中会把每个对象的标识放入一个"当前创建Bean池"中(DefaultSingletonBeanRegistry.singletonsCurrentlyInCreation),其代码片段如下:

Spring根据ObjectFactory创建实例对象的方法 --> DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory)
blogImg20190429102042.png
在创建对象时将beanName置入当前创建Bean池中 --> DefaultSingletonBeanRegistry.beforeSingletonCreation(String beanName)
创建对象池

有了上面"当前创建对象池的概念后,大家还要理解ObjectFactory这个概念,其详细信息大家可以百度一下自行了解。这里大概说一下,众所周知Spring在实例一个Bean的过程中,首先会根据合适的构造创建一个初步的实例对象,然后对此对象进行进一步的修饰(注入属性依赖、执行后处理器逻辑等操作),所有的操作都走完后才是一个Bean对象的完整创建流程。而ObjectFactory的概念就是就是上述的预创建的实例对象

同样的一段Bean的创建代码,我们看一下下面的3种情况:

public class CycleReference {
    public static void main(String[] args) {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("com/flash/springcode/application.xml"));
        CycleReferenceBeanA a = (CycleReferenceBeanA) beanFactory.getBean("beanA");
    }
}

1. 构造器循环依赖

通过构造器注入形成的循环依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示出现了循环依赖问题。

举例如下:
blogImg20190515150513.png

运行后结果:
blogImg20190515151033.png

其报错的原因较为容易理解:A和B的构造互相注入使得两对象根本无法进行基础的实例化操作,所以后续的操作无从谈起。

2. setter循环依赖

举例如下:
blogImg20190515154337.png

运行后结果:
blogImg20190515154815.png

其核心代码如下:

  1. 在创建对象beanA时(以下简称A),doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)方法中,发现对象beanA有循环依赖的情况,创建此对象的ObjectFactory并且以beanName为key储存起来。

blogImg20190517103322.png

blogImg20190515152611.png

  1. 在为A注入参数B时触发B的创建流程,在B的创建过程中注入参数A,此时因为beanA已经被创建会进入到getSingle()系列的方法,这里不再细说,其核心部分代码如下。

blogImg20190517103502.png

blogImg20190515153639.png

blogImg20190517103533.png

  1. 如上图红标所示,因为在创建A的时候已经将对应的ObjectFactory放入置singletonFactories中,所以此时直接返回了A的对象实例,B创建完成成功添加A的对象引用。当B创建完成后,A自然完成B的注入,A和B之间的互相引用就完成了。

3. prototype范围的循环依赖

非单例的bean对象如果有循环依赖,Spring也是无法解决的。其根本原因也不难理解。我们思考一下,单例对象的互相依赖解决的方案是根据构造函数创建一个初步的A对象后,将其先注入给B,后续将B反注入A。这个过程有一个非常关键的点:A对象在实例化之后,此对象在内存有且只有一个,而多例的Bean对象自然无法满足此条件。

转载于:https://www.cnblogs.com/ZzlevolFlash/p/10879841.html

猜你喜欢

转载自blog.csdn.net/weixin_30221655/article/details/94785828