01-需要复习一下前置知识:
02-什么是循环依赖?
解释:A对象需要创建B对象,随后创建B对象的时候发现需要创建A对象,
这个时候A对象并没有创建出来,他需要先创建B对象,但是B对象的创建又需要A对象的实例,相互依赖,那么就会构成一个循环。
03-Spring是如何解决循环依赖问题-逻辑?
1,解决问题核心思路:
2,对象的两种状态:
而我们又知道对象有两种状态
3,解决步骤:
我们截断最后一个步骤,阻断闭环,
用一个Map容器来存储对象,放入到我们Spring容器中
(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是通过三级村缓存来解决的循环依赖问题:
可能有小伙伴懵了,不是用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之前请记住以下重要实现方法:
创建流程中的几个核心方法
开始Debug:
在AbstractApplicationContext类中:
方法:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
断点处:
运行:
1,A与B对象没有创建:
打开beanFactory寻找一级缓存:
打开一级缓存发现无A或者B对象:
查看二级,三级缓存为空:
2,创建A和B对象
F7进入下一级:
一路向下进入到这里,
实例化剩下的单例对象:
F7深入:
查看分析:
里面包含了我们要创建的对象:
开始创建A对象:
3,进入到对象创建:
来补课:
F7深入:
(1)getBean()
@Override
public Object getBean(String name) throws BeansException {
//这个方法是实际获取bean的方法,也是触发依赖注入的方法
return doGetBean(name, null, null, false);
}
F7深入:
(2)doGetBean()
解释-Spring中的do:
所有以do开头的方法都是实际干活的方法,也就是核心处理逻辑。
beanName进行转换:
这里为空,说明参数是一定要进行创建的
F7深入:
这里不会只是一个参数,不会执行createBean,所以F7深入:
从单例工厂中获取对象
singletonFactory是谁?:
进入到create方法:
F7继续深入:
不断下一步F8,
一直到我们doCreateBean,真正的开始的创建对象了。
深入F7:
未完待续…