Spring 循环依赖与三级缓存源码debug

 

1、恶心的大厂面试题

被面试官暴打

1、你解释下spring中的三级缓存?

  • 2、三级缓存分别是什么?三个Map有什么异同?

3、什么是循环依赖?请你谈谈?看过 Spring源码吗?一般我们说的 Spring容器是什么?

4、如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?

5、多例的情况下,循环依赖问题为什么无法解决?

。。。。。。

 

2、什么是循环依赖?

多个 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 容器注入时候出现的问题

 

 

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

 

官网对循环依赖的说明

 

 

 

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

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

 

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

 

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

 

4、Spring容器循环依赖异常

4.1、通过代码理解循环依赖

循环依赖现象在 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 方式可以解决循环依赖问题

 

4.2、演示循环依赖异常

maven依赖    

<parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.3.3.RELEASE</version>

        <relativePath/>

    </parent>

环境搭建

1.A 类

package com.shuidiit.integration.service;

 

/**

 * @author by shizan

 * @Classname A

 * @Description TODO

 * @Date 2021/12/7 3:38 下午

 */

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

    }

 

}

 

2.B 类

package com.shuidiit.integration.service;

 

/**

 * @author by shizan

 * @Classname B

 * @Description TODO

 * @Date 2021/12/7 3:38 下午

 */

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

 

    }

 

}

 

3.ClientSpringContainer 类

 

package com.shuidiit.integration.service;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

/**

 * @author by shizan

 * @Classname ClientSpringContainer

 * @Description 只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,因为单例的时候只有一份,随时复用,那么就放到缓存里面

 * 而多例的bean,每次从容器中荻取都是—个新的对象,都会重B新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。

 * @Date 2021/12/7 3:39 下午

 */

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

    }

 

}

 

4.在 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">

    <!--

          1.spring容器默认的单例模式可以解决循环引用,单例默认支持

          2.spring容器原型依赖模式scope="prototype"多例模式下不能解决循环引用

      -->

 

    <!--depends-on 的意思就是当前这个bean如果要完成,先看depends-on指定的bean是否已经完成了初始化-->

    <!--scope="prototype"代表每次都要新建一次对象-->

 

    <bean id="a" class="com.shuidiit.integration.service.A">

        <property name="b" ref="b"/>

    </bean>

 

    <bean id="b" class="com.shuidiit.integration.service.B">

        <property name="a" ref="a"/>

    </bean>

 

</beans>

 

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

每个 bean 的 scope 实行默认不写就是 singleton

 

 

 

 

beanA 和 beanB 都创建成功了,程序没有抛异常

 

 

 

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

将 bean 的生命周期改为 prototype

 

 

抛异常

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?
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1697)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1442)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:342)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1115)
	at com.shuidiit.integration.service.ClientSpringContainer.main(ClientSpringContainer.java:16)
Caused by: 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?
	

 

 

 

 

4.3、循环依赖的解决办法

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

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

 

 

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

 

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

 

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

 

 

5、源码 Deug 前置知识

5.1、实例化 & 初始化

 

实例化和初始化的区别

 

1、实例化:堆内存中申请一块内存空间

 

 

 

2、初始化:完成属性的填充

 

 

 

 

5.2、3个Map & 4个方法

三级缓存 + 四大方法

 

 

三级缓存

第一级缓存:存放的是已经初始化好了的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,这也正是工厂思想的体现(工厂设计模式)

 

 

 

 

5.3、对象在三级缓存中的迁移

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

 

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

 

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

 

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

 

 

 

 

6、详细 Debug 流程

6.1、beanA 的实例化

 

技巧:如何阅读框架源码?答:打断点 + 看日志

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

 

 

 

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

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

 

 

 

 

 

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

 

 

 

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

refresh() 那一行

 

 

 

进入 refresh() 方法

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

 

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

 

 

 

进入 finishBeanFactoryInitialization(beanFactory) 方法

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

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

 

 

进入 beanFactory.preInstantiateSingletons() 方法

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

getBean(beanName) 那一行

 

 

 

进入 getBean(beanName) 方法

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

 

 

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

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

 

 

 

进入 getSingleton(beanName) 方法

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

 

 

 

 

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

 

 

 

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

 

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

getSingleton(beanName) 方法返回 null

 

 

 

 

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

 

 

 

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

 

 

 

 

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

 

 

 

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

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

 

 

 

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

 

 

 

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

 

 

 

 

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

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

 

 

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

 

 

 

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

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

 

 

 

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

这一看就是反射的操作啊

 

 

 

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

 

 

 

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

 

 

进入 instantiateBean(beanName, mbd) 方法

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

 

 

 

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

 

 

 

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

 

 

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

 

 

 

进入 BeanUtils.instantiateClass(constructorToUse) 方法

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

 

 

 

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

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

 

 

回到 instantiateBean(beanName, mbd) 方法中

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

 

 

 

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

 

 

 

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

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

 

 

 

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

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

 

 

 

获取并记录 A 的全类名

 

 

执行 BeanPostProcessor

 

 

 

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

 

 

 

 

step into后小手点击 addSingletonFactory

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

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

 

 

 

 

6.2、beanA 的属性填充

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

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

 

 

 

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

获取 beanA 的属性列表

 

 

 

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

 

 

 

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

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

 

 

 

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

 

 

 

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

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

 

 

 

进入 resolveReference(argName, ref) 方法

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

 

 

 

6.3、beanB 的实例化

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

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

 

 

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

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

 

 

 

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

 

 

 

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

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

 

 

 

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

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

获取到 beanB 的类型为  com.shuidiit.integration.service.B

 

 

 

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

 

 

 

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

老样子,创建 beanB 对应的 BeanWrapper instanceWrapper

 

 

 

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

调用 instantiateBean(beanName, mbd) 创建 beanWrapper

 

 

 

进入 instantiateBean(beanName, mbd) 方法

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

 

 

 

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

获取com.shuidiit.integration.service.B  的构造器,并将构造器信息记录在 

bd.resolvedConstructorOrFactoryMethod 字段中

 

 

 

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

 

 

 

进入 BeanUtils.instantiateClass(constructorToUse) 方法

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

 

 

 

回到 instantiateBean(beanName, mbd) 方法中

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

BeanWrapper bw 对象

 

 

 

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

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

 

 

 

执行 BeanPostProcessor 的处理过程

 

 

 

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

 

 

 

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

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

 

 

 

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

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

 

 

 

6.4、beanB 的属性填充

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

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

 

 

 

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

 

 

 

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

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

 

 

 

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

 

 

 

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

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

 

 

 

进入 resolveReference(argName, ref) 方法

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

 

 

 

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

 

 

 

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

 

 

 

进入 getSingleton(beanName, true) 方法

 

 

 

getSingleton(beanName) 调用了其重载方法 

getSingleton(beanName, true),接下来的逻辑很重要,让我们来唠嗑唠嗑

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

 

 

 

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

 

 

 

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

 

 

 

 

从单例工厂 singletonFactory 中获取 beanA;

将 beanA 添加至二级缓存 earlySingletonObjects 中;

将 beanA 从三级缓存 singletonFactories 中移除

 

 

 

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

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

 

 

 

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

 

 

 

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

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

 

 

 

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

 

 

 

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

 

 

 

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

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

 

 

 

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

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

 

 

 

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

此时 bw 包裹着 beanB,执行 bw.setPropertyValues(new MutablePropertyValues(deepCopy)) 方法会将 

deepCopy 中的元素依次赋值给 beanB 的各个属性,此时 beanB 中的 a 属性已经赋值为 beanA

 

 

 

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

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

 

 

 

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

singletonFactories 中获取 beanB

 

 

 

进入 getSingleton(beanName, false) 方法

由于传入的参数 allowEarlyReference = false,因此第三层 if 判断铁定进不去,而 beanB 在三级缓存 

singletonFactories 中存着,因此返回的 singletonObject 为 null

 

 

 

回到 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 已经填充完毕)

 

 

 

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

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

 

 

 

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

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

 

 

 

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

 

 

 

进入 addSingleton(beanName, singletonObject) 方法

① 将 beanB 放入一级缓存 singletonObjects 中

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

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

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

 

 

 

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

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

 

 

 

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

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

 

 

 

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

 

 

 

 

6.5、beanA 的属性填充

回到 resolveReference(argName, ref) 方法中

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

 

 

 

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

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

 

 

 

将 b 属性添加至 deepCopy 集合中

 

 

 

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

 

 

 

 

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

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

 

 

 

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

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

 

 

 

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

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

 

 

 

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

 

 

 

进入 getSingleton(beanName, false) 方法

之前 beanB 中注入 a 属性时,将 beanA 从三级缓存 singletonFactories 移动到了二级缓存 

earlySingletonObjects 中,因此可以从二级缓存 earlySingletonObjects 中获取到 beanA

 

 

 

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

最终将获取到的 beanA 返回

 

 

 

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

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

 

 

 

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

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

 

 

 

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

 

 

 

进入 addSingleton(beanName, singletonObject) 方法

① 将 beanA 放入一级缓存 singletonObjects 中

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

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

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

 

 

 

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

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

 

 

 

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

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

 

 

 

回到 preInstantiateSingletons() 方法中

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

 

 

 

 

 

7、循环依赖总结

7.1、全部 Debug 断点

导出 Debug 所有断点

点击【View Breakpoints】

 

 

 

7.2、Debug 步骤总结

循环依赖 Debug 的具体步骤

1、调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA

2、在getSingleton()方法中,从一级缓存中查找,没有,返回null

3、doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)

4、在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的creatBean()方法

5、进入AbstractAutowireCapableBeanFactory#doCreateBean(),先反射调用构造器创建出beanA的实例,然后判断。是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中〈即是否在第四步的集合中)。判断为true则将beanA添加到【三级缓存】中

6、对beanA进行属性填充,此时检测到beanA依赖于beanB,于是开始查找beanB

7、调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性

8、此时beanB依赖于beanA,调用getsingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的beanA

9、这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中

10、随后beanA继续他的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getsingleton()方法中继续向下执行,将beanA从二级缓存移动到一级缓存中

 

 

 

 

 

7.3、三级缓存总结

Spring 创建 Bean 的两大步骤

Spring创建bean主要分为两个步骤,创建原始bean对象,接着去填充对象属性和初始化

 

每次创建bean之前,我们都会从缓存中查下有没有该bean,因为是单例,只能有一个。当我们创建 beanA的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了beanB,接着就又去创建beanB,同样的流程,创建完 beanB填充属性时又发现它依赖了beanA又是同样的流程。

 

不同的是:这时候可以在三级缓存中查到刚放进去的原始对象beanA,所以不需要继续创建,用它注入beanB,完成beanB的创建既然 beanB创建好了,所以beanA就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成

 

Spring解决循环依赖依靠的是Bean的“中间态"这个概念,而这个中间态指的是已经实例化但还没初始化的状态,也即半成品。

 

实例化的过程又是通过构造器创建的,如果A还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决。

Spring 的三级缓存

Spring为了解决单例的循环依赖问题,使用了三级缓存:

1、其中一级缓存为单例池〈 singletonObjects)

2、二级缓存为提前曝光对象( earlySingletonObjects)

3、三级缓存为提前曝光对象工厂( singletonFactories)

@Nullable

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

   //从singletonObjects获取实例,singletonObjects中实例都是准备好的bean实例,可以直接使用

   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;

}

假设A、B循环引用,实例化A的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖A,这时候从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果有AOP代理,就进行AOP处理获取代理后的对象A,注入B,走剩下的流程。

 

 

 

 

参考资料 第 4 章 Spring_OnebyWang的博客-CSDN博客

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring循环依赖三级缓存是指在实例化Bean的过程中,Spring框架内部维护了三个缓存Map,分别是singletonObjects、earlySingletonObjects和singletonFactories。 1. singletonObjects缓存:存放已经完全初始化的Bean实例,包括在初始化过程中有循环依赖的Bean 2. earlySingletonObjects缓存:存放已经进行了实例化但是还未完成初始化的Bean实例,也包括在初始化过程中有循环依赖的Bean 3. singletonFactories缓存:存放Bean实例的Factory对象,用于解决循环依赖的问题,当需要解决循环依赖时,由Factory对象提供未完成的Bean实例。 通过这三个缓存Spring框架可以在Bean实例化的过程中解决循环依赖的问题。当Bean A依赖Bean B,同时Bean B也依赖Bean A时,Spring框架会通过缓存机制来避免无限递归的循环依赖。具体流程如下: 1. 当需要实例化Bean A时,Spring框架会先从singletonObjects缓存中查找是否已经存在该Bean实例,如果存在,则直接返回该实例。 2. 如果singletonObjects缓存中不存在Bean A实例,则会从earlySingletonObjects缓存中查找是否存在该实例。如果存在,则将其提供给Factory对象创建Bean A实例并返回。 3. 如果earlySingletonObjects缓存中也不存在Bean A实例,则会从singletonFactories缓存中查找是否存在该实例的Factory对象,如果存在,则使用该Factory对象创建Bean A实例并返回。 4. 如果singletonFactories缓存中也不存在Bean A实例的Factory对象,则需要创建一个新的Factory对象,同时将其存放到singletonFactories缓存中。这个新的Factory对象会提供一个未进行初始化的Bean A实例,同时缓存到earlySingletonObjects中。 5. 当Bean A实例被完全初始化后,会将其从earlySingletonObjects缓存中移动到singletonObjects缓存中,以供其他Bean依赖使用。 通过三级缓存的机制,Spring框架可以解决循环依赖的问题,并在Bean实例化过程中避免无限递归的循环依赖

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值