一. 什么是循环依赖?
多个bean之间相互依赖,形成了一个闭环,比如:A依赖于B、B依赖于C、C依赖于A
二:两种注入方式对循环依赖的影响
在通过IOC创建对象的时候,如果这个对象还有属性,会一并赋值进去,这个属性赋值有两种方式,构造器属性注入,set方法注入,
- 构造器属性注入: 构造器属性注入没有办法解决循环依赖
- set方法注入: 注入方式是set且单例,就不会有循环依赖问题
原型(Prototype)的场景是不支持循环依赖的,报错(BeanCurrentlylnCreationException)
三:spring是怎么判断一个类是否创建代理对象的
Spring会根据类的特性(是否实现接口、是否被切面所影响等)和配置(是否使用@Transactional注解等)来判断是否需要为某个类创建代理对象。
四:spring内部通过3级缓存来解决循环依赖
第一级缓存:〈也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象
第二级缓存: earlySingletonObjects,存放早期暴露出来的Bean对象,用于保存实例化完成的bean实例,用来解决普通对象创建过程中的循环依赖问题
第三级缓存: Map<String, ObiectFactory<?>> singletonFactories,存放可以生成Bean的工厂,用来解决存在 AOP 时的循环依赖问题
四:循环依赖具体流程
- 调用 doGetBean()方法,想要获取 beanA ,于是调用 getSingleton方法从缓存中查找beanA
- 在 getSingleton() 方法中,从一级缓存中查找,没有,返回 null
- doGetBean()方法中获取到 beanA 为 null ,于是走对应的处理逻辑,调用 getSingleton()的重载方法(参数为 Objectfactory 的)
- 在 getSingleton()方法中,先将 beanA_name 添加到一个集合中,用于标记该 bean 正在创建中,然后回调匿名内部类的 createBean 方法
- 进入 AbstractAutowireCapableBeanFactory#doCreateBean,先反射调用构造器创建出 beanA 的实例,然后判断,是否为单例,是否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否是在第四步的集合中)判断为 true 则将 beanA 添加到【三级缓存】中
- 对 beanA 进行属性填充,此时检测到 beanA 依赖于 beanC ,于是查找 beanC
- 调用 doGetBean() 方法,和上面 beanA 的过程一样,到缓存中查询 beanC ,没有则创建,然后给 beanC 填充属性
- 此时 beanC 依赖于 beanA,调用 getSingleton()获取 beanA.依次从一级、二级、三级缓存中找、此时从三级缓存中获取到 beanA 的创建工厂,通过创建工厂获取到 singletonObject ,此时这个 singletonObject 指向的就是上面在 doCreateBean()方法中实例化的 beanA
- 这样 beanC 就获取到了 beanA 的依赖,于是 beanC 顺利完成初始化,并将 beanA 从三级缓存移动到二级缓存中
- 随后 beanA 继续他的属性填充工作,此时也获取到了 beanC ,beanA 也随之完成了创建,回到 getSingleton()方法中继续向下执行,将 beanA 从二级缓存移动到一级缓存中
五:Spring解决循环依赖一定需要三级缓存吗?
二级缓存也能解决循环依赖的问题,但是如果完全依靠二级缓存解决循环依赖,意味着当我们依赖了一个代理类的时候,就需要在Bean实例化之后完成AOP代理。而在Spring的设计中,为了解耦Bean的初始化和代理,是通过后置处理器来在Bean生命周期的最后一步来完成AOP代理的。Spring为了不破坏AOP的代理设计原则,则引入第三级缓存,在三级缓存中保存对象工厂,有了它,在后续发生循环依赖时,如果依赖的Bean被AOP代理,那么通过这个工厂获取到的就是代理后的对象,如果没有被AOP代理,那么这个工厂获取到的就是实例化的真实对象。