• 面试题:Spring循环依赖问题


    Spring是怎么解决循环依赖的?

    首先,Spring 解决循环依赖有两个前提条件:

    1. 不全是构造器方式的循环依赖
    2. 必须是单例

    基于上面的问题,我们知道Bean的生命周期,本质上解决循环依赖的问题就是三级缓存,通过三级缓存提前拿到未初始化的对象。

    第一级缓存:用来保存实例化、初始化都完成的对象

    第二级缓存:用来保存实例化完成,但是未初始化完成的对象

    第三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象

    image-20210111234717861

    假设一个简单的循环依赖场景,A、B互相依赖。

    image-20210111234733721

    A对象的创建过程:

    1. 创建对象A,实例化的时候把A对象工厂放入三级缓存

    image-20210111234804804

    1. A注入属性时,发现依赖B,转而去实例化B

    2. 同样创建对象B,注入属性时发现依赖A,一次从一级到三级缓存查询A,从三级缓存通过对象工厂拿到A,把A放入二级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入一级缓存。

    image-20210111234852793

    1. 接着继续创建A,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除二级缓存中的A,同时把A放入一级缓存

    2. 最后,一级缓存中保存着实例化、初始化都完成的A、B对象

    image-20210111234918883

    因此,由于把实例化和初始化的流程分开了,所以如果都是用构造器的话,就没法分离这个操作,所以都是构造器的话就无法解决循环依赖的问题了。

    具体的方法:

    • 调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查询beanA
    • getSingleton()方法屮,从一级缓存中查找,没有,返回null
    • doGetBean()方法屮获取到的beanA为null, 于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
    • 在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记bean创建中。然后回调匿名内部类的creatBean方法
    • 进入AbstractAutowireCapableBeanFactory#doCreateBean,先反射调用构选器创建出beanA的实例。然后判断:是否为单例、足否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否在第四步的集合中)。判断力true则将beanA添加到【三级缓存】中。
    • 对beanA进行属性填充,此时检测到 beanA依赖beanB,于是开始查找beanB
    • 调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没行则创建,然后给beanB填充属性。
    • 此时beanB依赖于beanA,调用getSingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此这个 singletonObject指向的就是在上面在在doCreateBean()方法中实例化的beanA 。
    • 这样beanB就获取到beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中。
    • 随后 beanA继续他的属性填充工作,此时也获取到beanB, beanA也随之完成创建,回到getSingleton()方法屮继续向下执行,将beanA从二级缓存移动到一级缓存中。

    image-20210112001717811

    为什么要三级缓存?二级不行吗?

    不可以,主要是为了生成代理对象。

    因为三级缓存中放的是生成具体对象的匿名内部类,value为一个lambda表达式,他可以生成代理对象,也可以是普通的实例对象。

    使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。

    假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致了。

    image-20210111235041954

  • 相关阅读:
    分布式事务-第一刀
    Qt
    自描述C++部分面试题集
    读书笔记6.21
    STL vector容器 和deque容器
    C++ STL框架
    C++ 多态
    C++ 虚继承
    C++ 类的继承和派生
    C++ 类中的函数重载
  • 原文地址:https://www.cnblogs.com/dalianpai/p/14265090.html
Copyright © 2020-2023  润新知