01-需要复习一下前置知识:

02-什么是循环依赖?

spring的遍历标签 spring如何解决循环_spring


解释:A对象需要创建B对象,随后创建B对象的时候发现需要创建A对象,

这个时候A对象并没有创建出来,他需要先创建B对象,但是B对象的创建又需要A对象的实例,相互依赖,那么就会构成一个循环。

spring的遍历标签 spring如何解决循环_java_02

03-Spring是如何解决循环依赖问题-逻辑?

1,解决问题核心思路:

spring的遍历标签 spring如何解决循环_spring_03

2,对象的两种状态:

而我们又知道对象有两种状态

spring的遍历标签 spring如何解决循环_spring的遍历标签_04

3,解决步骤:

我们截断最后一个步骤,阻断闭环,

用一个Map容器来存储对象,放入到我们Spring容器中

spring的遍历标签 spring如何解决循环_spring_05

(1)A(半)

实例化A时候,由于B没有实例化为空,所以A只能完成一个实例化,并没有完成初始化,只能算是一个半成品,这个时候将“A(半成品)”放入到Map映射中。
Map映射也在Spring的容器之中。

(2)B(半)

与上面A半成品一样,没有完成初始化,只完成实例化,所以把B半成品也放入Map映射中。

(3)B(成品)

判断中,我们在容器中找到A(半成品)的地址,将A(半成品)的对象引用赋值给B对象的A属性,完成初始化操作,因此B完成了初始化操作,成为一个成品对象。

(4)A(成品)

当B的成品对象完成之后,逆着链路,追寻到最初需要创建B对象的应用场景中(A对象初始化中),将B(成品)的对象引用赋值给,A中的B对象属性。

03-Spring是如何解决循环依赖问题-代码:

如果对于Spring有学习有一定基础就一定会知道的,Spring是通过三级村缓存来解决的循环依赖问题:

spring的遍历标签 spring如何解决循环_java_06


可能有小伙伴懵了,不是用Map来解决吗?怎么又到三级缓存了?那么我们先来看一个map在Spring中使用的实现类DefaultSingletonBeanRegistry :

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	/** Maximum number of suppressed exceptions to preserve. */
	private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;


	/** 一级缓存
	 * 		用于保存BeanName和创建的Bean实例之间的关系
	 *
	 *  Cache of singleton objects: bean name to bean instance.
	 *  翻译:
	 *  	单例对象缓存:bean实例的bean的名称 */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/**
	 * 三级缓存:
	 * 		用于保存BeanName和创建Bean的工厂之间的关系
	 *
	 * Cache of singleton factories: bean name to ObjectFactory.
	 * 	翻译:
	 * 		单例对象缓存:Bean工厂的bean名称 */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/**
	 * 二级缓存:
	 * 		保存Bean名称和创建的Bean实例之间的关系,与 singletonFactories的不同之处在于,
	 * 	当一个单例Bean放在这里之后,那么Bean还在创建过程中就可以通过getBean方法获取到,、
	 * 	可以方便进行循环依赖的检测
	 *
	 * 	Cache of early singleton objects: bean name to bean instance.
	 *   翻译:
	 *   	早期单里的对象的缓存:bean 实力名称的实例化 */
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

看到了,我们所说简单粗暴的理解三个Map组成,Spring的三级缓存。用来解决循环依赖问题。
三个Map的详细用途,我认为上面注释已经写得很清楚。
但是关于三级缓存泛型中ObjectFactory<?>还是要解释一下:

ObjectFactory是一个函数式接口,是一个Lambda表达式,可以通过getObject获取(JDK1.8新特性)
当做一个参数传递到方法中,一般情况下传的Lambda表达式,可以通过getObject方法来执行Lambda表达式。

03-构建代码debug测试环境:

测试类A:

package xyz.taichu.ioc;

/**
 * 循环依赖测试类A
 * 		与B构成循环依赖度
 *
 * @author
 * @site
 * @company
 * @create 2021-11-28 22:11
 */
public class A {

	private B b;

	public B getB() {
		return b;
	}

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

	@Override
	public String toString() {
		return "A{" +
				"b=" + b +
				'}';
	}
}

测试类B:

package xyz.taichu.ioc;

/**
 *  循环依赖测试类B,与A构成循环依赖
 *
 * @author
 * @site
 * @company
 * @create 2021-11-28 22:11
 */
public class B {
	private A a;

	public A getA() {`在这里插入代码片`
		return a;
	}

	public void setA(A a) {
		this.a = a;
	}

	@Override
	public String toString() {
		return "B{" +
				"a=" + a +
				'}';
	}
}

XML配置文件:

<?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">

	<!--	循环依赖问题:-->
	<bean id="a"  class="xyz.taichu.ioc.A">
		<property name="b" ref="b"></property>
	</bean>

	<bean id="b" class="xyz.taichu.ioc.B">
		<property name="a" ref="a"></property>
	</bean>


</beans>
public static void main(String[] args) {
		ApplicationContext applicationContext=new ClassPathXmlApplicationContext("/Cycle/cycle.xml");
		A bean=applicationContext.getBean(A.class);
		System.out.println(bean.getB());

		B bean2=applicationContext.getBean(B.class);
		System.out.println(bean2.getA());
	}

04-Debug:

Debug之前请记住以下重要实现方法:

创建流程中的几个核心方法

spring的遍历标签 spring如何解决循环_spring的遍历标签_07

开始Debug:

spring的遍历标签 spring如何解决循环_gradle_08

在AbstractApplicationContext类中:
方法:

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

断点处:

spring的遍历标签 spring如何解决循环_spring的遍历标签_09


运行:

spring的遍历标签 spring如何解决循环_gradle_10

1,A与B对象没有创建:

打开beanFactory寻找一级缓存:

spring的遍历标签 spring如何解决循环_java_11


打开一级缓存发现无A或者B对象:

spring的遍历标签 spring如何解决循环_java_12

查看二级,三级缓存为空:

spring的遍历标签 spring如何解决循环_spring_13

2,创建A和B对象

F7进入下一级:

spring的遍历标签 spring如何解决循环_java_14


一路向下进入到这里,

实例化剩下的单例对象:

spring的遍历标签 spring如何解决循环_gradle_15


F7深入:

spring的遍历标签 spring如何解决循环_java_16


spring的遍历标签 spring如何解决循环_spring的遍历标签_17


查看分析:

里面包含了我们要创建的对象:

spring的遍历标签 spring如何解决循环_spring的遍历标签_18

开始创建A对象:

spring的遍历标签 spring如何解决循环_java_19

3,进入到对象创建:

spring的遍历标签 spring如何解决循环_java_20


来补课:

spring的遍历标签 spring如何解决循环_实例化_21


F7深入:

(1)getBean()
@Override
	public Object getBean(String name) throws BeansException {
		//这个方法是实际获取bean的方法,也是触发依赖注入的方法
		return doGetBean(name, null, null, false);
	}

spring的遍历标签 spring如何解决循环_spring_22


F7深入:

(2)doGetBean()
解释-Spring中的do:

所有以do开头的方法都是实际干活的方法,也就是核心处理逻辑。

beanName进行转换:

spring的遍历标签 spring如何解决循环_spring的遍历标签_23


spring的遍历标签 spring如何解决循环_spring的遍历标签_24


这里为空,说明参数是一定要进行创建的

spring的遍历标签 spring如何解决循环_实例化_25


F7深入:

spring的遍历标签 spring如何解决循环_实例化_26


spring的遍历标签 spring如何解决循环_实例化_27

这里不会只是一个参数,不会执行createBean,所以F7深入:

spring的遍历标签 spring如何解决循环_spring_28

从单例工厂中获取对象

spring的遍历标签 spring如何解决循环_gradle_29


singletonFactory是谁?:

spring的遍历标签 spring如何解决循环_spring的遍历标签_30

进入到create方法:

spring的遍历标签 spring如何解决循环_spring_31


F7继续深入:

spring的遍历标签 spring如何解决循环_spring_32

不断下一步F8,

一直到我们doCreateBean,真正的开始的创建对象了。

spring的遍历标签 spring如何解决循环_gradle_33


深入F7:

spring的遍历标签 spring如何解决循环_gradle_34

未完待续…