Spring IOC循环依赖问题
什么是循环依赖
循环依赖其实就是循环引用, 也就是两个或者两个以上的Bean互相持有对方,形成闭环,例如:A依赖B,B依赖C,C又依赖于A.
Spring中循环依赖的场景有:
- 构造器的循环依赖(构造器注入)
- Field属性的循环依赖(Set注入)
其中,构造器的循环依赖问题是无法解决的,只能抛出异常BeanCurrentlyInCreationException异常,在解决属性循环依赖时.Spring采用的是提前暴露对象的方法. 这也就解决了其中一个依赖的问题,继而解决了循环依赖的问题.
Spring IOC处理循环依赖的方式
在Spring IOC中主要是通过三级缓存机制来处理循环依赖问题.
- 第一季缓存singletonObjects:单例池(key->beanName, value->Bean)
- 这个缓存中都是一些已经实例化好的对象,不存在依赖问题.
- 第二季缓存:earlySingletonObjects(key->beanName, value->Bean)
- 已经实例化但还没有进行属性注入的Bean,由三级缓存放入.
- 第三季缓存:singletonFactories(key->beanName, value->ObjectFacotry)
- singletonFactory.getObject()出来的对象.该对象还未开始依赖或者暂未依赖完全.
示例:ClassA依赖ClassB,ClassB依赖ClassA,这样就构成了循环依赖,Spring IOC的解决方案如下:
Spring IOC解决循环依赖的理论依据是基于Java的引用传递,当获得对象的引用时,对象的属性可以延后设置.
但是,构造器必须时在获取引用之前,Spring通过Set或者@Autowired方法解决循环依赖其实是通过提前暴露一个ObjectFactory对象来完成的,简单来说ClassA在调用构造器完成初始化之后,在调用setClassB方法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中.
- Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器中
- ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB, 此时ClassB不在Spring容器中
- Spring容器开始初始化ClassB,同时也会将ClassB提前暴露在Spring容器中
- ClassB调用setClassA方法,Spring从容器中获取ClassA,因为ClassA已经暴露,此时可以从第三级缓存中获取到ClassA,这样就完成了ClassB的实例化.
- ClassA这个时候通过Spring容器获取ClassB实例,完成ClassA的实例化.
这个时候,ClassA和ClassB都完成对象的初始化,解决了循环依赖的问题.
什么情况下无法解决循环依赖
- 单例Bean
- 构造器注入形成的循环依赖无法解决
- Set注入形成的循环依赖可以解决
- @Autowired注解方式注入形成的循环依赖可以解决
- prototype原型的Bean(多例)
- 对于原型bean的初始化过程中不论是那种情况产生的循环依赖,Spring都会直接报错处理.
参考: Spring IOC循环依赖问题_蹦跶的蜗牛的博客-CSDN博客_springioc循环依赖Spring IOC循环依赖问题_蹦跶的蜗牛的博客-CSDN博客_springioc循环依赖Spring IOC循环依赖问题_蹦跶的蜗牛的博客-CSDN博客_springioc循环依赖
本文含有隐藏内容,请 开通VIP 后查看