一、循环依赖
什么是循环依赖:它发生在bean A依赖于另一个bean B时,bean B依赖于bean A;
简单讲就是,类和类之间的依赖关系产生了闭环,就会导致循环依赖问题的发生。
具体代码如下:
@Bean
public class A {
@Autowire
private B b;
}
@Bean
public class B {
@Autowire
private C c;
}@Bean
public class C {
@Autowire
private A a;
}
二、发生循环依赖时,Spring会发生哪些情况
当Spring加载bean时,会按照我们的工作需要进行对Bean的创建,例如:
【spring|Spring如何解决循环依赖问题】beanA ----> beanB---->beanC
先创建beanC,之后再创建beanB,最后再创建beanA,当beanB创建完成后将其注入beanC中,beanA创建好之后将其注入beanB中。
但当存在循环依赖时,Spring因不知道先创建哪一个bean,因此会产生循环依赖问题的发生。所以会引发BeanCurrentlyInCreationException异常。
三、Spring如何解决循环依赖问题
Spring通过三级缓存解决了循环依赖的问题;
Spring内部有三级缓存:
1.singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例
2.earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例
3.singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。
当类与类之间需要循环引用时:
1.在A进行实例化以后,使用A实例化后的对象去创建一个对象工厂,将其添加至三级缓存当中,表示A已经进行实例化了;
2.当A注?属性时,发现依赖B,B未被创建,所以去实例化B,当B注入属性时,发现同样C未被创建,再去实例化C;
3.当C注入A时,它就会从缓存里找A对象。依次从?级到三级缓存查询A,从三级缓存通过对象??拿到A,发现A虽然不太完善,但是存在,把A放??级缓存,同时删除三级缓存中的A,此时,C已经实例化并且初始化完成,把C放入?级缓存,A注入B,B注入C也是这样进行的;
4.接着A继续属性赋值,顺利从?级缓存拿到实例化且初始化完成的B对象,同理B也从一级缓存拿到实例化且初始化完成的C对象,A对象创建也完成,删除?级缓存中的A,同时把A放??级缓存;
5.最后,?级缓存中保存着实例化、初始化都完成的A、B、C对象。
Spring之所以能解决setter注入的循环依赖,时因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的化,那么都得在实例化这一步完成注入,所以自然是无法支持了。
四、二级缓存为什么不能解决循环依赖问题
因为往二级缓存中放一个普通的Bean对象,Bean在初始化时,会通过BeanPostProcessor 去?成代理对象,之后会覆盖掉二级缓存中的普通Bean对象,会导致读取的Bean不一致。
而三级缓存中放的是生成对象的匿名内部类,在获取Object时,既可以生成代理对象,也可以返回普通对象。保证使用的是同一个Bean对象。
以上是对Spirng解决以来循环问题见解,与君共勉!
推荐阅读
- java|Spring boot——Actuator 详解
- java|JAVA Stream的collect用法与原理(详解)
- spring|在IntelliJ IDEA里创建Spring Boot项目
- Spring|Spring Cloud OpenFeign/Hystrix 超时配置
- spring|Spring Cloud OpenFeign 使用介绍
- java|SpringCloud OpenFeign的功能与使用
- SpringCloud|SpringCloud -Ribbon
- java|SpringCloud OpenFeign + Nacos
- MyBatis|mybatis-config.xml-配置文件详解