美文网首页
Spring 循环依赖处理

Spring 循环依赖处理

作者: 向梦而来 | 来源:发表于2020-12-03 10:23 被阅读0次

什么是循环依赖

循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 A 依赖 B,B 依赖 C,C 依赖 A。在初始化 A 的时候发现引用了 B,这时就会去初始化B,然后又发现 B 引用 C,跑去初始化 C,初始化 C 的时候发现引用了 A,则又会去初始化 A,依次循环永不退出,除非有终结条件。

Spring 循环依赖的场景有两种:

  • 构造器的循环依赖:Spring无法解决
  • field 属性的循环依赖:Spring只解决scope为singleton的循环依赖,解决singleton bean的核心就在于提前曝光 bean,对于scope 为 prototype 的 bean,Spring无法解决,直接抛出BeanCurrentlyInCreationException 异常。

解决方法

先从加载 bean最初始的方法 AbstractBeanFactory的#doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) 方法开始。在#doGetBean(...) 方法中,首先会根据 beanName 从单例 bean 缓存中获取,如果不为空则直接返回。代码如下:

Object sharedInstance = getSingleton(beanName);

调用getSingleton方法,从单例缓存中获取

//这个方法主要是从三个缓存中获取,分别是:singletonObjects、earlySingletonObjects、singletonFactories
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从单例缓冲中加载 bean
    Object singletonObject = this.singletonObjects.get(beanName);
    // 缓存中的 bean 为空,且当前 bean 正在创建
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 加锁
        synchronized (this.singletonObjects) {
            // 从 earlySingletonObjects 获取
            singletonObject = this.earlySingletonObjects.get(beanName);
            // earlySingletonObjects 中没有,且允许提前创建
            if (singletonObject == null && allowEarlyReference) {
                // 从 singletonFactories 中获取对应的 ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 获得 bean
                    singletonObject = singletonFactory.getObject();
                    // 添加 bean 到 earlySingletonObjects 中
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 从 singletonFactories 中移除对应的 ObjectFactory
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

三级缓存定义:

/** 一级缓存,存放的是单例 bean 的映射,这里的 bean 是已经创建完成的
*  对应关系为 bean name --> bean instance
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 二级缓存,存放的是早期半成品(没有进行属性填充和初始化)的 bean
*  对应关系为 bean name --> bean instance
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 三级缓存,存放的是 ObjectFactory,可以理解为创建早期半成品bean的factory,最终添加到二级缓存
* 对应关系是 bean name --> ObjectFactory
*/
private final Map<String, Object> earlySingletonObjects =new ConcurrentHashMap<>(16);

singletonObject = singletonFactory.getObject();
从三级缓存 singletonFactories 获取。如果获取到,则通过其 #getObject() 方法,获取对象,那么是什么时候把缓存加入到singletonFactories呢?一直往下跟会发现在 #doCreateBean方法中,有这么一段代码:

// AbstractAutowireCapableBeanFactory.java

boolean earlySingletonExposure = (mbd.isSingleton() // 单例模式
        && this.allowCircularReferences // 运行循环依赖
        && isSingletonCurrentlyInCreation(beanName)); // 当前单例 bean 是否正在被创建
if (earlySingletonExposure) {
    if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    // 提前将创建的 bean 实例加入到 singletonFactories 中
    // 这里是为了后期避免循环依赖
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

//当一个 Bean 满足三个条件:单例、运行提前暴露bean、当前bean正在创建中,则调用#addSingletonFactory方法,将它添加到缓存中
// DefaultSingletonBeanRegistry.java

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 其实已经被创建出来了,但是它还不是很完美(没有进行属性填充和初始化),但是对于其他依赖它的对象而言已经足够了(可以根据对象引用定位到堆中对象),能够被认出来了。

20170912082357749.jpeg
  • 首先 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 完成初始化。到这里整个链路就已经完成了初始化过程了
循环依赖.png

相关文章

  • Spring 循环依赖问题fix

    Spring 循环依赖问题fix 拆分的时候,把error都处理完后,准备把工程起起来,发现spring的循环依赖...

  • 一文详解Spring中的循环依赖,面试必杀技

    目录 前言 什么是循环依赖? 什么情况下循环依赖可以被处理? Spring是如何解决的循环依赖? 简单的循环依赖(...

  • Spring 循环依赖处理

    什么是循环依赖 循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 ...

  • spring 循环依赖处理

    解决 bean 之间的循环依赖分为2种: 构造函数注入导致的循环依赖发现这种情况, spring无解, 直接抛出 ...

  • Spring处理循环依赖

    什么是循环依赖 循环依赖指的是多个对象之间的依赖关系形成一个闭环。 下图展示了两个对象 A 和 B 形成的一个循环...

  • Spring 是如何解决循环依赖的?

    Spring 是如何解决循环依赖的? 循环依赖: Spring 循环依赖有三种情况: 构造器的循环依赖,这种依赖 ...

  • Spring-IOC-循环依赖检测与Bean的创建

    Spring容器的循环依赖检测 Spring容器循环依赖包括:构造器循环依赖和setter循环依赖。 1- 构造器...

  • 浅析Spring循环依赖处理

    背景正在学习Spring源码。微信公众号的推文中有一篇循环依赖相关文章,但是只有大概处理过程,没有具体的流程。借此...

  • spring 循环依赖处理流程

    在使用Spring开发的时候,如果要在类的某个方法的执行前后添加一些特殊逻辑处理,我们往往会使用aspect或者m...

  • Spring之循环依赖及解决方式

    1.Spring循环依赖 循环依赖指Spring对象之间的循环引用,最终形成死循环。举例: A依赖于B,B依赖于C...

网友评论

      本文标题:Spring 循环依赖处理

      本文链接:https://www.haomeiwen.com/subject/owzpwktx.html