细聊Spring的循环依赖|八月更文挑战

247 阅读19分钟

小伙伴们,大家好啊,这里是经典鸡翅。鸡翅老哥今天想给大家聊聊Spring的循环依赖,大厂必问有没有?看完这篇保你会!

什么是循环依赖

多个 bean 之间相互依赖,形成了一个闭环 比如:A 依赖于 B、B 依赖于 C、C 依赖于 A。

public class CircularDependency {
    class A {
        B b;
    }
    
    class B {
        C c;
    }
    
    class C {
        A a;
    }
}

通常来说,如果问 Spring 容器内部如何解决循环依赖, 一定是指默认的单例 Bean 中,属性互相引用的场景。也就是说,Spring 的循环依赖,是 Spring 容器注入时候出现的问题。

两种注入方式对循环依赖的影响

官网对循环依赖的说明

image.png 两种注入方式对循环依赖的影响

构造器注入:容易造成无法解决的循环依赖,不推荐使用(If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.)

Setter 注入:推荐使用 setter 方式注入单例 bean

结论:我们 AB 循环依赖问题只要 A 的注入方式是 setter 且 singleton,就不会有循环依赖问题

Spring容器循环依赖异常

通过代码理解循环依赖

循环依赖现象在 Spring 容器中 注入依赖的对象,有 2 种情况。

构造器方式注入依赖

代码

1、ServiceA

@Component
public class ServiceA {

    private ServiceB serviceB;

    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

2、ServiceB

@Component
public class ServiceB {

    private ServiceA serviceA;

    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

3、ClientConstructor

/**
 * 通过构造器的方式注入依赖,构造器的方式注入依赖的bean,下面两个bean循环依赖
 *
 * 测试后发现,构造器循环依赖是无法解决的
 */
public class ClientConstructor {
    public static void main(String[] args) {
        new ServiceA(new ServiceB(new ServiceA(new ServiceB()))); ....
    }
}

结论:构造器注入没有办法解决循环依赖, 你想让构造器注入支持循环依赖,是不存在的。如果构造器能够解决循环依赖问题,那么我就可以无限套娃~

形象理解:各自实例化时都需要对方实例,这就类似于死锁,如果不采取一种办法解决,那么它们将永远互相等待下去

Setter 方式注入

代码

1、ServiceA

@Component
public class ServiceA {

    private ServiceB serviceB;

    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
        System.out.println("A 里面设置了B");
    }
}

2、ServiceB

@Component
public class ServiceB {

    private ServiceA serviceA;

    public void setServiceA(ServiceA serviceA) {
        this.serviceA = serviceA;
        System.out.println("B 里面设置了A");
    }
}

3、ClientConstructor

public class ClientSet {
    public static void main(String[] args) {

        //创建serviceA
        ServiceA serviceA = new ServiceA();

        //创建serviceB
        ServiceB serviceB = new ServiceB();

        //将serviceA注入到serviceB中
        serviceB.setServiceA(serviceA);

        //将serviceB注入到serviceA中
        serviceA.setServiceB(serviceB);

    }
}

结论:setter 方式可以解决循环依赖问题

演示循环依赖异常

A 类

public class A {
    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }

    public A() {
        System.out.println("---A created success");
    }
}

B 类

public class B {
    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
    
    public B() {
        System.out.println("---B created success");

    }
}

ClientSpringContainer 类

public class ClientSpringContainer {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        A a = context.getBean("a", A.class);
        B b = context.getBean("b", B.class);
    }
}

在 resources 文件夹下创建 applicationContext.xml 文件,对 bean 中的属性进行注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--
        1.spring容器默认的单例模式可以解决循环引用,单例默认支持
        2.spring容器原型依赖模式scope="prototype"多例模式下不能解决循环引用
    -->

    <!--depends-on 的意思就是当前这个bean如果要完成,先看depends-on指定的bean是否已经完成了初始化-->
    <!--scope="prototype"代表每次都要新建一次对象-->
    
    <bean id="a" class="com.heygo.spring.circulardependency.A">
        <property name="b" ref="b"/>
    </bean>

    <bean id="b" class="com.heygo.spring.circulardependency.B">
        <property name="a" ref="a"/>
    </bean>

</beans>

scope = “singleton”,默认的单例(Singleton)的场景是支持循环依赖的,不报错。

每个 bean 的 scope 实行默认不写就是 singleton,beanA 和 beanB 都创建成功了,程序没有抛异常。

image.png scope = “prototype”,原型(Prototype)的场景是不支持循环依赖的,报错。

将 bean 的生命周期改为 prototype。

image.png

啊哦,抛异常了:Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'a' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'b' while setting bean property 'b'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

image.png

循环依赖的解决办法

重要结论:Spring 内部通过 3 级缓存来解决循环依赖。

所谓的三级缓存其实就是 Spring 容器内部用来解决循环依赖问题的三个 Map,这三个 Map 在 DefaultSingletonBeanRegistry 类中。

image.png

第一级缓存:Map<String, Object> singletonObjects,我愿称之为成品单例池,常说的 Spring 容器就是指它,我们获取单例 bean 就是在这里面获取的,存放已经经历了完整生命周期的Bean对象。

第二级缓存:Map<String, Object> earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整,可以认为是半成品的 bean)。

第三级缓存:Map<String, ObiectFactory<?>> singletonFactories,存放可以生成Bean的工厂,用于生产(创建)对象。

源码 Deug 前置知识

实例化 & 初始化

实例化和初始化的区别 实例化:堆内存中申请一块内存空间

image.png 初始化:完成属性的填充

image.png

3个Map & 4个方法

三级缓存 + 四大方法

image.png

三级缓存

第一级缓存:存放的是已经初始化好了的Bean,bean名称与bean实例相对应,即所谓的单例池。表示已经经历了完整生命周期的Bean对象。

第二级缓存:存放的是实例化了,但是未初始化的Bean,bean名称与bean实例相对应。表示Bean的生命周期还没走完(Bean的属性还未填充)就把这个Bean存入该缓存中。也就是实例化但未初始化的bean放入该缓存里。

第三级缓存:表示存放生成bean的工厂,存放的是FactoryBean,bean名称与bean工厂对应。假如A类实现了FactoryBean,那么依赖注入的时候不是A类,而是A类产生的Bean。

四大方法

getSingleton():从容器里面获得单例的bean,没有的话则会创建 bean。

doCreateBean():执行创建 bean 的操作(在 Spring 中以 do 开头的方法都是干实事的方法)。

populateBean():创建完 bean 之后,对 bean 的属性进行填充。

addSingleton():bean 初始化完成之后,添加到单例容器池中,下次执行 getSingleton() 方法时就能获取到。

注:关于三级缓存 Map<String, ObjectFactory<?>> singletonFactories的说明,singletonFactories 的 value 为 ObjectFactory 接口实现类的实例。ObjectFactory 为函数式接口,在该接口中定义了一个 getObject() 方法用于获取 bean,这也正是工厂思想的体现(工厂设计模式)。

image.png

对象在三级缓存中的迁移

A/B 两对象在三级缓存中的迁移说明。

A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B。

B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A,然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。

B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。

详细 Debug 流程

beanA 的实例化

在 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 代码处打上断点,逐步执行(Step Over),发现执行 new ClassPathXmlApplicationContext("applicationContext.xml") 操作时,beanA 和 beanB 都已经被创建好了,因此我们需要进入 new ClassPathXmlApplicationContext("applicationContext.xml") 中。

image.png

进入 new ClassPathXmlApplicationContext("applicationContext.xml") 中。

点击 Step Into,首先进入了静态代码块中,不管我们的事,使用 Step Out 退出此方法。

image.png 再次 Step Into,进入 ClassPathXmlApplicationContext 类的构造函数,该构造函数使用 this 调用了另一个重载构造函数。

image.png

继续 Step Into,进入重载构造函数后单步 Step Over,发现执行完 refresh() 方法后输出如下日志,于是我们将断点打在 refresh() 那一行。

image.png

进入 refresh() 方法。

Step Into 进入 refresh() 方法,发现执行完 finishBeanFactoryInitialization(beanFactory) 方法后输出日志,于是我们将断点打在 finishBeanFactoryInitialization(beanFactory) 那一行。

从注释也可以看出本方法完成了非懒加载单例 bean的初始化(Instantiate all remaining (non-lazy-init) singletons.)。

image.png

进入 finishBeanFactoryInitialization(beanFactory) 方法。

Step Into 进入 finishBeanFactoryInitialization(beanFactory) 方法,发现执行完 beanFactory.preInstantiateSingletons() 方法后输出日志,于是我们将断点打在 beanFactory.preInstantiateSingletons() 那一行。

从注释也可以看出本方法完成了非懒加载单例 bean的初始化(Instantiate all remaining (non-lazy-init) singletons.)。

image.png

进入 beanFactory.preInstantiateSingletons() 方法。

Step Into 进入 beanFactory.preInstantiateSingletons() 方法,发现执行完 getBean(beanName) 方法后输出日志,于是我们将断点打在 getBean(beanName) 那一行。

image.png

进入 getBean(beanName) 方法。

getBean(beanName) 调用了 doGetBean(name, null, null, false) 方法,也就是前面说过的:在 Spring 里面,以do 开头的方法都是干实事的方法。

image.png

进入 doGetBean(name, null, null, false) 方法。

我们可以给 bean 配置别名,这里的 transformedBeanName(name) 方法就是将用户别名转换为 bean 的真实名称。

image.png

进入 getSingleton(beanName) 方法。

有必要讲一下 getSingleton(beanName) 方法。

image.png

调用了其重载的方法,allowEarlyReference == true 表示可以从三级缓存 earlySingletonObjects 中获取 bean,allowEarlyReference == false 表示不可以从三级缓存 earlySingletonObjects 中获取 bean。

image.png

getSingleton(beanName, true) 方法尝试从一级缓存 singletonObjects 中获取 beanA,beanA 现在还没有开始造呢(isSingletonCurrentlyInCreation(beanName) 返回 false),获取不到返回 null。

image.png

回到 doGetBean(name, null, null, false) 方法中。

getSingleton(beanName) 方法返回 null。

image.png

我们所说的 bean 对于 Spring 来说就是一个个的 RootBeanDefinition 实例。

image.png

这个 dependsOn 变量对应于 bean 的 depends-on="" 属性,我们没有配置过,因此为 null。

image.png

转了一圈发现并没有 beanA,终于要开始准备创建 beanA 啦。

image.png

进入 getSingleton(beanName, () -> {... } 方法。

在 IDEA 2020 中,点击 Step Into 可以手动选择要进入的方法,因此我们需要使用鼠标左键点击 getSingleton() 方法。

image.png

首先尝试从一级缓存 singletonObjects 获取 beanA,那肯定是获取不到的啦,因此 singletonObject == null,那么就需要创建 beanA,此时日志会输出:【Creating shared instance of singleton bean ‘a’】。

image.png

当执行完 singletonObject = singletonFactory.getObject(); 时,会输出【—A created success】,这说明执行 singletonFactory.getObject() 方法时将会实例化 beanA,并且根据代码变量名可得知单例工厂创建的,这个单例工厂就是我们传入的 Lambda 表达式。

image.png 进入 createBean(beanName, mbd, args) 方法。

我们 Step Into 进入 createBean(beanName, mbd, args) 方法中,mbdToUse 将用于创建 beanA。

image.png

来了,终于要执行 doCreateBean(beanName, mbdToUse, args) 实例化 beanA 啦。

image.png

进入 doCreateBean(beanName, mbdToUse, args) 方法。

Step Into 进入 doCreateBean(beanName, mbdToUse, args) 方法,在 factoryBeanInstanceCache 中并不存在 beanA 对应的 Wrapper 缓存,instanceWrapper == null,因此我们要去创建 beanA 对应的 instanceWrapper,Wrapper 由包裹之意思,instanceWrapper 翻译过来为实例包裹器的意思,形象理解为:beanA 实例化需要经过 instanceWrapper 之手,beanA 实例被 instanceWrapper 包裹在其中。

image.png

进入 createBeanInstance(beanName, mbd, args) 方法。

这一看就是反射的操作啊。

image.png

这里有个 resolved 变量,写着注释:Shortcut when re-creating the same bean…,我个人理解是 resolved 标志该 bean 是否已经被实例化了,如果已经被实例化了,那么 resolved == true,这样就不用重复创建同一个 bean 了。

image.png

Candidate constructors for autowiring? 难道是构造器自动注入?在 return 的时候调用 instantiateBean(beanName, mbd) 方法实例化 beanA,并将其返回。

image.png

进入 instantiateBean(beanName, mbd) 方法。

getInstantiationStrategy().instantiate(mbd, beanName, this) 方法完成了 beanA 的实例化。

image.png 进入 getInstantiationStrategy().instantiate(mbd, beanName, this) 方法

首先获取已经解析好的构造器 bd.resolvedConstructorOrFactoryMethod,这是第一次创建,当然还没有啦,因此 constructorToUse == null。然后获取 A 的类型,如果发现是接口则直接抛异常。最后获取 A 的公开构造器,并将其赋值给 bd.resolvedConstructorOrFactoryMethod。

image.png

获取构造器的目的当然是为了实例化 beanA 啦。

image.png

进入 BeanUtils.instantiateClass(constructorToUse) 方法。

通过构造器创建 beanA 实例,Step Over 后会输出:【—A created success】,并且会回到 getInstantiationStrategy().instantiate(mbd, beanName, this) 方法中。

image.png

回到 getInstantiationStrategy().instantiate(mbd, beanName, this) 方法中。

在 BeanUtils.instantiateClass(constructorToUse) 方法中创建好了 beanA 实例,不过还没有进行初始化,可以看到属性 b = null,Step Over 后会回到 instantiateBean(beanName, mbd) 方法中。

image.png

回到 instantiateBean(beanName, mbd) 方法中。

得到刚才创建的 beanA 实例,但其属性并未被初始化。

image.png

将实例化的 beanA 装进 BeanWrapper 中并返回 bw。

image.png

回到 createBeanInstance(beanName, mbd, args) 方法中。

得到刚才创建的 beanWrapper 实例,该 beanWrapper 包裹(封装)了刚才创建的 beanA 实例。

image.png

回到 doCreateBean(beanName, mbdToUse, args) 方法中。

在 doCreateBean(beanName, mbdToUse, args) 方法获得 BeanWrapper instanceWrapper,用于封装 beanA 实例。

image.png

获取并记录 A 的全类名。

image.png 执行 BeanPostProcessor

image.png

如果该 bean 是单例 bean(mbd.isSingleton()),并且允许循环依赖(this.allowCircularReferences),并且当前 bean 正在创建过程中(isSingletonCurrentlyInCreation(beanName)),那么就就允许提前暴露该单例 bean(earlySingletonExposure = true),则会执行 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)) 方法将该 bean 放到三级缓存 singletonFactories 中。

image.png

进入 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)) 方法。

首先去一级缓存 singletonObjects 中找一下有没有 beanA,肯定没有啦~然后将 beanA 添加到三级缓存 singletonFactories 中,并将 beanA 从二级缓存 earlySingletonObjects 中移除,最后将 beanName 添加至 registeredSingletons 中,表示该 bean 实例已经被注册。

image.png

beanA 的属性填充

回到 doCreateBean(beanName, mbdToUse, args) 方法中。

接着回到 doCreateBean(beanName, mbdToUse, args) 方法中,需要执行 populateBean(beanName, mbd, instanceWrapper) 方法对 beanA 中的属性进行填充。

image.png

进入 populateBean(beanName, mbd, instanceWrapper) 方法。

获取 beanA 的属性列表。

image.png

执行 applyPropertyValues(beanName, mbd, bw, pvs) 方法完成 beanA 属性的填充。

image.png

进入 applyPropertyValues(beanName, mbd, bw, pvs) 方法。

获取到 beanA 的属性列表,发现有个属性为 b。

image.png

遍历每一个属性,并对每一个属性进行注入,valueResolver.resolveValueIfNecessary(pv, originalValue) 的作用:Given a PropertyValue, return a value, resolving any references to other beans in the factory if necessary.

image.png

进入 valueResolver.resolveValueIfNecessary(pv, originalValue) 方法。

image.png 通过 resolveReference(argName, ref) 解决依赖注入的问题。

进入 resolveReference(argName, ref) 方法。

先获得属性 b 的名称,再通过 this.beanFactory.getBean(resolvedName) 方法获取 beanB 的实例。

image.png

beanB 的实例化

进入 this.beanFactory.getBean(resolvedName) 方法。

哦,这熟悉的 doGetBean(name, null, null, false) 方法,这就开始递归了呀。

image.png

再次执行 doGetBean(name, null, null, false) 方法。

beanB 还没有实例化,因此 getSingleton(beanName) 方法返回 null。

image.png

呐,又来到了这个熟悉的地方,先尝试获取 beanB 实例,获取不到就执行 createBean() 的操作。

image.png

进入 getSingleton(beanName, () -> {... } 方法。

首先尝试从一级缓存 singletonObjects 中获取 beanB,那肯定是获取不到的呀。

image.png

然后就调用 singletonFactory.getObject() 创建 beanB。

image.png

进入 createBean(beanName, mbd, args) 方法。

获取到 beanB 的类型为 com.heygo.spring.circulardependency.B。

image.png

之前创建 beanA 的时候没有看到,现在看到挺有趣的:Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. 也就是说我们可以通过 BeanPostProcessors 返回 bean 的代理,而非 bean 本身。然后喜闻乐见,又来到了 doCreateBean(beanName, mbdToUse, args) 环节。

image.png

进入 doCreateBean(beanName, mbdToUse, args) 方法。 老样子,创建 beanB 对应的 BeanWrapper instanceWrapper。

image.png

进入 createBeanInstance(beanName, mbd, args) 方法。

调用 instantiateBean(beanName, mbd) 创建 beanWrapper。

image.png

进入 instantiateBean(beanName, mbd) 方法。

调用 getInstantiationStrategy().instantiate(mbd, beanName, this) 创建 beanWrapper。

image.png

进入 getInstantiationStrategy().instantiate(mbd, beanName, this) 方法。

获取 com.heygo.spring.circulardependency.B 的构造器,并将构造器信息记录在 bd.resolvedConstructorOrFactoryMethod 字段中。

image.png

调用 BeanUtils.instantiateClass(constructorToUse) 方法创建 beanB 实例。

image.png

进入 BeanUtils.instantiateClass(constructorToUse) 方法。

通过调用 B 类的构造器创建 beanB 实例,此时控制台会输出:【—B created success】。

image.png

回到 instantiateBean(beanName, mbd) 方法中。

在 instantiateBean(beanName, mbd) 方法中得到创建好的 beanB 实例,并将其丢进 beanWrapper 中,封装为 BeanWrapper bw 对象。

image.png

回到 doCreateBean(beanName, mbdToUse, args) 方法中。

createBeanInstance(beanName, mbd, args) 方法将返回包装着 beanB 的 beanWrapper。

image.png

执行 BeanPostProcessor 的处理过程。

image.png

beanB 由于满足单例并且正在被创建,因此 beanB 可以被提前暴露出去(在属性还未初始化的时候可以提前暴露出去),于是执行 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)) 方法将其添加至三级缓存 singletonFactory 中。

image.png

进入 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)) 方法。

将 beanB 实例添加至三级缓存 singletonFactory 中,从二级缓存 earlySingletonObjects 中移除,并注册其 beanName。

image.png

回到 doCreateBean(beanName, mbdToUse, args) 方法中。

执行 populateBean(beanName,mbd,instancewrapper) 方法填充 beanB 的属性。

image.png

beanB 的属性填充

进入 populateBean(beanName, mbd, instanceWrapper) 方法。

执行 mbd.getPropertyValues() 方法获取 beanB 的属性列表。

image.png

执行 applyPropertyValues(beanName, mbd, bw, pvs) 方法完成 beanB 属性的填充。

image.png

进入 applyPropertyValues(beanName, mbd, bw, pvs) 方法。

执行 mpvs.getPropertyValuelist() 方法获取 beanB 的属性列表。

image.png

遍历每一个属性,并对每一个属性进行注入,valueResolver.resolveValueIfNecessary(pv, originalValue) 的作用:Given a PropertyValue, return a value, resolving any references to other beans in the factory if necessary.

image.png

进入 valueResolver.resolveValueIfNecessary(pv, originalValue) 方法。

执行 resolveReference(argName, ref) 方法为 beanB 注入名为 a 属性。

image.png

进入 resolveReference(argName, ref) 方法。

执行 this.beanFactory.getBean(resolvedName) 方法获取 beanA 实例,其实就是执行 doGetBean(name, null, null, false) 方法。

image.png

进入 doGetBean(name, null, null, false) 方法。

关键来了,这里执行 getSingleton(beanName) 是够能够获取到 beanA 实例呢?答案是可以。

image.png

进入 getSingleton(beanName, true) 方法。

getSingleton(beanName) 调用了其重载方法 getSingleton(beanName, true),接下来的逻辑很重要,让我们来唠嗑唠嗑。

beanA 并没有存放在一级缓存 singletonObjects 中,因此执行 Object singletonObject = this.singletonObjects.get(beanName) 后,singletonObject == null,再加上 beanA 正在满足创建的条件(isSingletonCurrentlyInCreation(beanName) == true),因此可以进入第一层 if 判断。

image.png

beanA 被存放在三级缓存 singletonFactories 中,从二级缓存 earlySingletonObjects 中获取也是 null,因此可以进入第二层 if 判断。

image.png

从三级缓存中获取 beanA 肯定不为空啦~,因此可以进入第三层 if 判断。

image.png

①从单例工厂 singletonFactory 中获取 beanA;②将 beanA 添加至二级缓存 earlySingletonObjects 中;③将 beanA 从三级缓存 singletonFactories 中移除。

image.png

回到 doGetBean(name, null, null, false) 方法中。

执行 Object sharedInstance = getSingleton(beanName) 将获得之前存入三级缓存 singletonFactories 中的 beanA。

image.png

好家伙,获取到 beanA 后就直接返回了。

image.png

回到 applyPropertyValues(beanName, mbd, bw, pvs) 方法中。

执行 valueResolver.resolveValueIfNecessary(pv, originalValue) 方法获取到 beanA 实例。

image.png

将属性 beanA 添加到 deepCopy 集合中(List deepCopy = new ArrayList<>(original.size()))。

image.png

执行 bw.setPropertyValues(new MutablePropertyValues(deepCopy)) 方法将会填充 beanB 中的 a 属性。

image.png

进入 bw.setPropertyValues(new MutablePropertyValues(deepCopy)) 方法。

调用了其重载方法 setPropertyValues(pvs, false, false)。

image.png

进入 setPropertyValues(pvs, false, false) 方法。

在该方法中会对 bean 的每一个属性进行填充(通过 setPropertyValues(pvs, false, false) 方法对属性进行赋值)。

image.png

回到 applyPropertyValues(beanName, mbd, bw, pvs) 方法中。

此时 bw 包裹着 beanB,执行 bw.setPropertyValues(new MutablePropertyValues(deepCopy)) 方法会将 deepCopy 中的元素依次赋值给 beanB 的各个属性,此时 beanB 中的 a 属性已经赋值为 beanA。

image.png

回到 doCreateBean(beanName, mbdToUse, args) 方法中。

因为 instanceWrapper 封装了 beanB,所以执行了 populateBean(beanName, mbd, instanceWrapper) 方法后,beanB 中的 a 属性就已经被填充啦~可以看到 beanB 中有 beanA,但 beanA 中没有 beanB。

image.png

执行 getSingleton(beanName, false) 方法,传入的参数 allowEarlyReference = false,表示不允许从三级缓存 singletonFactories 中获取 beanB。

image.png

进入 getSingleton(beanName, false) 方法。

由于传入的参数 allowEarlyReference = false,因此第三层 if 判断铁定进不去,而 beanB 在三级缓存 singletonFactories 中存着,因此返回的 singletonObject 为 null。

image.png

回到 doCreateBean(beanName, mbdToUse, args) 方法中。

这里应该是执行 bean 的 destroy-method ,应该只会在工厂销毁的时候并且 bean 为单例的条件下,其内部逻辑才会执行。registerDisposableBeanIfNecessary(beanName, bean, mbd) 方法的注释如下:Add the given bean to the list of disposable beans in this factory, registering its DisposableBean interface and/or the given destroy method to be called on factory shutdown (if applicable). Only applies to singletons. 最后将 beanB 返回(属性 a 已经填充完毕)。

image.png

回到 createBean(beanName, mbd, args) 方法。

执行 doCreateBean(beanName, mbdToUse, args) 方法得到包装 beanB 实例(属性 a 已经填充完毕),并将其返回。

image.png

回到 getSingleton(beanName, () -> { ... } 方法中。

执行 singletonFactory.getObject() 方法获取到 beanB 实,这里的 singletonFactory 是之前调用 getSingleton(beanName, () -> { ... } 方法传入的 Lambda 表达式,然后将 newSingleton 设置为 true。

image.png

执行 addSingleton(beanName, singletonObject) 方法将 beanB 实例添加到一级缓存 singletonObjects 中。

image.png

进入 addSingleton(beanName, singletonObject) 方法。

① 将 beanB 放入一级缓存 singletonObjects 中

② 将 beanB 从三级缓存 singletonFactories 中删除(beanB 确实在三级缓存中)

③ 将 beanB 从二级缓存 earlySingletonObjects 中删除(beanB 并不在二级缓存中)

④ 将 beanB 的 beanName 注册到 registeredSingletons 中(之前添加至三级缓存的时候已经注册过啦~)

image.png

回到 getSingleton(beanName, () -> { ... } 方法中。

执行 addSingleton(beanName, singletonObject) 将 beanB 添加到一级缓存 singletonObjects 后,将 beanB 返回。

image.png

回到 doGetBean(name, null, null, false) 方法中。

执行完 getSingleton(beanName, () -> { ... } 方法后,得到属性已经填充好的 beanB,并且已经将其添加至一级缓存 singletonObjects 中。

image.png

将 beanB 返回,想想返回到哪儿去了呢?当初时因为 beanA 要填充其属性 b,才执行了创建 beanB 的操作,现在返回肯定是将 beanB 返回给 beanA。

image.png

beanA 的属性填充

回到 resolveReference(argName, ref) 方法中

执行完 this.beanFactory.getBean(resolvedName) 方法后,获得了属性填充好的 beanB 实例,并将其实例返回。

image.png

回到 applyPropertyValues(beanName, mbd, bw, pvs) 方法中

执行完 valueResolver.resolveValueIfNecessary(pv, originalValue) 方法后,将获得属性填充好的 beanB 实例。

image.png

将 b 属性添加至 deepCopy 集合中。

image.png

执行 bw.setPropertyValues(new MutablePropertyValues(deepCopy)) 方法对 beanA 的 b 属性进行填充。

image.png

进入 setPropertyValues(pvs, false, false) 方法

在 bw.setPropertyValues(new MutablePropertyValues(deepCopy)) 方法中调用了 setPropertyValues(pvs, false, false) 方法,在该方法中会对 bean 的每一个属性进行填充(通过 setPropertyValues(pvs, false, false) 方法对属性进行赋值)。

image.png

回到 applyPropertyValues(beanName, mbd, bw, pvs) 方法中

此时 bw 中包裹着 beanA,,执行 bw.setPropertyValues(new MutablePropertyValues(deepCopy)) 方法会将 deepCopy 中的元素依次赋值给 beanA 的各个属性,此时 beanA 中的 b 属性已经赋值为 beanA,又加上之前 beanB 中的 a 属性已经赋值为 beanA,此时可开启无限套娃模式。

image.png

回到 doCreateBean(beanName, mbdToUse, args) 方法中

执行完 populateBean(beanName, mbd, instanceWrapper) 方法后,可以开启无限套娃模式 。

image.png

这次执行 getSingleton(beanName, false) 方法能获取到 beanA 吗?

image.png

进入 getSingleton(beanName, false) 方法

之前 beanB 中注入 a 属性时,将 beanA 从三级缓存 singletonFactories 移动到了二级缓存 earlySingletonObjects 中,因此可以从二级缓存 earlySingletonObjects 中获取到 beanA。

image.png

回到 doCreateBean(beanName, mbdToUse, args) 方法中。

最终将获取到的 beanA 返回。

image.png

回到 createBean(beanName, mbd, args) 方法中。

执行 doCreateBean(beanName, mbdToUse, args) 方法后得到 beanA 实例,并将此实例返回。

image.png

回到 getSingleton(beanName, () -> { ... } 方法。

执行 singletonFactory.getObject() 方法后将获得 beanA 实例,这里的 singletonFactory 是我们传入的 Lambda 表达式(专门用于创建 bean 实例)。

image.png

执行 addSingleton(beanName, singletonObject) 方法将 beanA 添加到一级缓存 singletonObjects 中。

image.png

进入 addSingleton(beanName, singletonObject) 方法

① 将 beanA 放入一级缓存 singletonObjects 中

② 将 beanA 从三级缓存 singletonFactories 中删除(beanA 并不在三级缓存中)

③ 将 beanA 从二级缓存 earlySingletonObjects 中删除(beanA 确实在二级缓存中)

④ 将 beanA 的 beanName 注册到 registeredSingletons 中(之前添加至三级缓存的时候已经注册过啦~)

image.png

回到 getSingleton(beanName, () -> { ... } 方法中

将 beanA 添加至一级缓存 singletonObjects 后,将其返回。

image.png

回到 doGetBean(name, null, null, false) 方法中

执行 getSingleton(beanName, () -> { ... } 方法得到 beanA 实例后,将其返回。

image.png

回到 preInstantiateSingletons() 方法中

终于要结束了。。。执行完 getBean(beanName) 方法后,将得到无限套娃版本的 beanA 和 beanB 实例。

image.png

Debug 步骤总结

循环依赖 Debug 的具体步骤

调用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.png