是什么?

循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 A 依赖 B,B 依赖 C,C 依赖 A。

循环依赖,其实就是一个死循环的过程,在初始化 A 的时候发现引用了 B,这时就会去初始化 B,然后又发现 B 引用 C,跑去初始化 C,初始化 C 的时候发现引用了 A,则又会去初始化 A,依次循环永不退出,除非有终结条件

spring在singleton模式下,用三级缓存解决此问题,在原型(prototype)模式下无法解决。

参见官网

image-20210307180948965.png

源码解析

DefaultSingletonBeanRegistry类

image-20210307181551212.png

第一层singletonObjects存放的是已经初始化好了的Bean,
第二层earlySingletonObjects存放的是实例化了,但是未初始化的Bean,
第三层singletonFactories存放的是FactoryBean。假如A类实现了FactoryBean,那么依赖注入的时候不是A类而是A类产生的Bean。

1A创建过程中需要B,于是A将自己放到三级缓里面,去实例化B
2B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
3B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。

debug

image.png

步入方法

image.png

出来重新进去

image.png

步入

image.png

refresh()方法加载ioc容器 在这里设置断点

image.png

对象创建成功

image.png

进入refresh()方法

一步一步走 看控制台是否打印了创建对象信息

image.png

慢慢追踪断点进入的方法:

image.png

doGetBean() 真正做实际事的方法

image.png

getSongleton()查找一级缓存 一级缓存为null

image-20210307190747798

3个map 4个方法image.png

ioc底层只认识root bean ,好比所有的对象都是object

image.png

全部的断点:

image.png

总结

1、调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA在getSingleton()方法中,从一级缓存中查找,没有,返回null
doGetBean()方法中获取到的beanA的null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
2、在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该beani正在创建中。然后回调匿名内部类的creatBean方法
3、进入AbstractAutowireCapableBeanFactory#doCreateBean,先反射调用构造器创建出beanA的实例,然后判断。是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中〈即是否在第四步的集合中)。判断为true则将beanA添加到三级缓存中
4、对beanA进行属性填充,此时检测到beanA依赖于beanB,于是开始查找beanB
5、调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性。此l时beanB依赖于beanA,调用getSingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的beanA。这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中。随后beanA继续他的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getSingleton()方法中继续下执行,将beanA从二级缓存移动到一级缓存中。

image.png

bean的加载流程图

image.png

所果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。