幸运时时彩官方网spring5 源码深度解析— IOC 之 循环依赖处理

  • 时间:
  • 浏览:0
  • 来源:森七博客 - 专注共享张凯博客资讯

那先 是循环依赖

循环依赖我我人太好全都 循环引用,也全都 有一4个多多 或则有一4个多多 以上的b幸运时时彩官方网ean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图所示:

注意,这可不可以不能 函数的循环调用,是对象的相互依赖关系。循环调用我我人太好全都 有一4个多多 死循环,除非有终结条件。

Spring中循环依赖场景有:

(1)构造器的循环依赖

(2)field属性的循环依赖。 

对于构造器的循环依赖,Spring 是无法解决的,不可不可以抛出 BeanCurrentlyInCreationException 异常表示循环依赖,全都下面亲们分析的可不可以 基于 field 属性的循环依赖。

Spring 只解决 scope 为 singleton 的循环依赖,对于scope 为 prototype 的 bean Spring 无法解决,直接抛出 BeanCurrentlyInCreationException 异常。

怎么才能 才能 检测循环依赖

检测循环依赖相对比较容易,Bean在创建的完后 可不可以不能给该Bean打标,将会递归调用回来发现正在创建中励志的话 ,即说明了循环依赖了。

解决循环依赖

亲们先从加载 bean 最初始的法子  doGetBean() 完后 刚结束。

在 doGetBean() 中,首先会根据 beanName 从单例 bean 缓存中获幸运时时彩官方网取,将会不为空则直接返回。

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;
}

这个法子 主全都 从有一4个多多 缓存中获取,分别是:singletonObjects、earlySingletonObjects、singletonFactories,三者定义如下:

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

这三级缓存分别指:

(1)singletonFactories : 单例对象工厂的cache

(2)earlySingletonObjects :提幸运时时彩官方网前暴光的单例对象的Cache

(3)singletonObjects:单例对象的cache

亲们全都 Spring 解决 singleton bean 的关键因素所在,我称亲们为三级缓存,第一级为 singletonObjects,第二级为 earlySingletonObjects,第三级为 singletonFactories。这里亲们可不可以不能通过 getSingleton() 看一遍亲们是怎么才能 才能 配合的,这分析该法子 完后 ,提下其中的 isSingletonCurrentlyInCreation() 和 allowEarlyReference

  • isSingletonCurrentlyInCreation():判断当前 singleton bean 是否存在创建中。bean 存在创建中也全都 说 bean 在初始化但是这么 完成初始化,有有一4个多多 完后 的过程我我人太好和 Spring 解决 bean 循环依赖的理念相辅相成,将会 Spring 解决 singleton bean 的核心就在于提前曝光 bean。
  • allowEarlyReference:从字面意思上端理解全都 允许提前拿到引用。我我人太好真正的意思是是否允许从 singletonFactories 缓存中通过 getObject() 拿到对象,为那先 会有完后 有一4个多多 字段呢?原因分析分析 就在于 singletonFactories 才是 Spring 解决 singleton bean 的诀窍所在,这个亲们后续分析。

getSingleton() 整个过程如下:首先从一级缓存 singletonObjects 获取,将会这么 且当前指定的 beanName 正在创建,就再从二级缓存中 earlySingletonObjects 获取,将会还是这么 获取到且运行 singletonFactories 通过 getObject() 获取,则从三级缓存 singletonFactories 获取,将会获取到则,通过其 getObject() 获取对象,并将其加入到二级缓存 earlySingletonObjects 中 从三级缓存 singletonFactories 删除,如下:

singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);

完后 就从三级缓存升级到二级缓存了。

上端是从缓存中获取,但是缓存中的数据从哪里添添加来的呢?一4个多多劲往下跟会发现在 doCreateBean() ( AbstractAutowireCapableBeanFactory ) 中,有这么 一段代码:

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isDebugEnabled()) {
        logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
    }
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

也全都 亲们上一篇文章中讲的最后一每项,提前将创建好但还未进行属性赋值的的Bean装入 缓存中。

将会 earlySingletonExposure == true 励志的话 ,则调用 addSingletonFactory() 将亲们添加到缓存中,但是有一4个多多 bean 要具备如下条件才会添加至缓存中:

  • 单例
  • 运行提前暴露 bean
  • 当前 bean 正在创建中

addSingletonFactory() 代码如下:

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);
        }
    }
}

从这段代码亲们可不可以不能看出 singletonFactories 这个三级缓存才是解决 Spring Bean 循环依赖的诀窍所在。一块儿这段代码存在在 createBeanInstance() 法子 完后 ,也全都 说这个 bean 我我人太好将会被创建出来了,但是它可不可以 很完美(这么 进行属性填充和初始化),但是对于一点依赖它的对象而言将会足够了(可不可以不能根据对象引用定位到堆中对象),可不可以被认出来了,全都 Spring 在这个完后 选者将该对象提前曝光出来让亲们认识认识。

介绍到这里亲们发现三级缓存 singletonFactories 和 二级缓存 earlySingletonObjects 中的值可不可以 出处了,那一级缓存在哪里设置的呢?在类 DefaultSingletonBeanRegistry 中可不可以不能发现这个 addSingleton() 法子 ,源码如下:

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

添加至一级缓存,一块儿从二级、三级缓存中删除。这个法子 在亲们创建 bean 的链路暗含哪个地方引用呢?其我我人太好前面博客 LZ 将会提到过了,在 doGetBean() 解决不同 scope 时,将会是 singleton,则调用 getSingleton(),如下:

if (mbd.isSingleton()) {
    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;
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
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) {
            //....
            try {
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            //.....
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

至此,Spring 关于 singleton bean 循环依赖将会分析完毕了。全都亲们基本可不可以不可不可以不能选者 Spring 解决循环依赖的方案了:Spring 在创建 bean 的完后 并可不可以 等它全部完成,全都 在创建过程中将创建中的 bean 的 ObjectFactory 提前曝光(即加入到 singletonFactories 缓存中),完后 一旦下有一4个多多 bean 创建的完后 可不可以 依赖 bean ,则直接使用 ObjectFactory 的 getObject() 获取了,也全都  getSingleton()中的代码片段了。

到这里,关于 Spring 解决 bean 循环依赖就将会分析完毕了。最但是描述下就上端那个循环依赖 Spring 解决的过程:首先 A 完成初始化第一步并将一点人提前曝光出来(通过 ObjectFactory 将一点人提前曝光),在初始化的完后 ,发现一点人依赖对象 B,此时就会去尝试 get(B),这个完后 发现 B 还这么 被创建出来,但是 B 就走创建流程,在 B 初始化的完后 ,同样发现一点人依赖 C,C 也这么 被创建出来,这个完后 C 又完后 刚结束初始化线程池,但是在初始化的过程中发现一点人依赖 A,于是尝试 get(A),这个完后 将会 A 将会添加至缓存中(一般可不可以 添加至三级缓存 singletonFactories ),通过 ObjectFactory 提前曝光,全都可不可以不能通过 ObjectFactory.getObject() 拿到 A 对象,C 拿到 A 对象后顺利完成初始化,但是将一点人添加到一级缓存中,回到 B ,B 可不可以不可不可以拿到 C 对象,完成初始化,A 可不可以不能顺利拿到 B 完成初始化。到这里整个链路就将会完成了初始化过程了。