一、循环依赖产生的原因
简单描述一下啥叫循环依赖,简单来说就是俄罗斯套娃,你中有我,我中有你。
圆和三角分别表示两个bean对象,圆里面引用了三角,三角里面引用了圆,这个就是循环依赖所产生的结果了。
在实际业务场景中其实也很多见,比如两个服务,一个是用户服务,一个是订单服务。
1.用户会通过自己的账号来查询自己的订单信息,这时候一种方案就需要在用户服务里面调用订单服务查找特定用户的订单列表。
2.商家会通过所有的订单信息按照用户进行分组,这时候就需要在订单服务里面调用用户服务来查找一批订单对应的用户的详细信息。
由于业务边界有时候很难划分清楚,就会伴随这种循环依赖的产生。但是同学们可能会说,可是我平常@Autowired的时候是那么自由自在,无拘无束,从不报错,那是因为spring已经帮我们解决了这个问题了。
二、代码准备
1.InstanceA
public class InstanceA {
private InstanceB b;
public InstanceB getB() {
return b;
}
public void setB(InstanceB b) {
this.b = b;
}
}
复制代码
2.InstanceB
public class InstanceB {
private InstanceA a;
public InstanceA getA() {
return a;
}
public void setA(InstanceA a) {
this.a = a;
}
}
复制代码
3.aa.xml配置文件
复制代码
4.main函数
public static void main(String[] args) {
ApplicationContext xc = new ClassPathXmlApplicationContext("aa.xml");
InstanceA instanceA = xc.getBean(InstanceA.class);
}
复制代码
三、图解+源码
我一直在想用文字如何描述比较好,考虑下来还是把关键点一步一步地和大家一起来看下,最后把流程图也给大家贴出来,如果描述不清楚的可以配合流程图来进行调试。
首先需要大家清楚的是三级缓存其实指的就是3个map,这三个名字请大家务必记牢
下面我们开始进入调试
1.找到入口方法refresh()
找到 refresh() 方法,这个是我们bean实例化的入口,也是我们本次调试循环依赖的入口
2.进入refresh()找到执行实例化动作的finishBeanFactoryInitialization(beanFactory)
3.进入finishBeanFactoryInitialization(beanFactory)找到beanFactory.preInstantiateSingletons()
4.preInstantiateSingletons()方法内部找到getBean(beanName)
1. 通过循环来分别执行InstanceA和InstanceB的实例化和初始化
2.经过一系列判断,进入else逻辑找到getBean(beanName)
//判断是否是抽象类/单例/懒加载
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//判断是否继承FactoryBean
if (isFactoryBean(beanName))
复制代码
5.通过getBean(beanName)找到doGetBean进入其实现类重点看下getSingleton(beanName)
进入到getSingleton(beanName)里面,这个方法需要大家记住,我们实例化好的和初始化好的对象都会经过这个方法来从各级缓存中去尝试读取。
/** Names of beans that are currently in creation */
private final Set singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
复制代码
这个集合需要记一下,用于记录当前处于创建阶段的bean的beanname,由于当前我们在实例化a的时候还没有进入到创建阶段,仅仅只是去缓存中尝试获取a,如果存在就直接拿到了,现在是不存在,所以返回null
6.由于一级缓存中不存在a,并且a也不处于create阶段,所以此时需要去触发a的实例化
7.进去getObject()进去匿名内部类中,createBean(beanName, mbd, args)
8.找到doCreateBean(beanName, mbdToUse, args)方法
9.进入doCreateBean(beanName, mbdToUse, args)
找到下面这个方法,这个方法的作用就是把a及其匿名内部类放入三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
复制代码
1.判断传入的ObjectFactory是否为空
2.给一级缓存加锁
3.判断一级缓存中是否存在a,显然是不存在的
4.把a及其ObjectFactory放入singletonFactories也就是三级缓存中
到这一步我们的三级缓存中终于有东西进来了现在的状态如下图
bean a目前是这种状态,所以接下来需要把a里面的元素填充好
10 这里我们已经把a放入了三级缓存,开始填充bean
找到关键行
这里我们稍微跳一跳,因为中间涉及的校验操作比较多。
进入populateBean
找到最后一行
applyPropertyValues(beanName, mbd, bw, pvs);
复制代码
毫不犹豫的进入,啥也不管,冲就完事,跑到这个for循环
11.去缓存中去找b是否存在
找到这行关键代码
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
复制代码
一直跑我们发现最后进到了我们最初的doGetBean里面
是不是似曾相识很熟悉的感觉,找到老乡了。
参考:《2020最新Java基础精讲视频教程和学习路线!》
链接:https://juejin.cn/post/694269...