Spring 是怎么处理循环依赖的?
2022-02-12 19:35
441 查看
Java语法中的循环依赖
首先看一个使用构造函数的循环依赖,如下:
public class ObjectA { private ObjectB b; public ObjectA(ObjectB b) { this.b = b; } } public class ObjectB { private ObjectA a; public ObjectB(ObjectA a) { this.a = a; } } public class Main { public static void main(String[] args) { //ObjectB b = new ObjectB(new ObjectA(new ObjectB())); } }
大家可以看上面这个例子,可以看出是没有办法new出ObjectA或者ObjectB的
那怎么解决上面的例子呢?如下:
public class ObjectA { private ObjectB b; public void setB(ObjectB b) { this.b = b; } public ObjectB getB() { return this.b; } } public class ObjectB { private ObjectA a; public void setA(ObjectA a) { this.a = a; } public ObjectA getA() { return this.a; } } public class Main { public static void main(String[] args) { ObjectB b = new ObjectB(); ObjectA a = new ObjectA(); b.setA(a); a.setB(b); System.out.println(a + " " + a.getB()); System.out.println(b + " " + b.getA()); } }
输出如下:
cn.eagle.li.spring.dependence.demo1.ObjectA@4534b60d cn.eagle.li.spring.dependence.demo1.ObjectB@3fa77460 cn.eagle.li.spring.dependence.demo1.ObjectB@3fa77460 cn.eagle.li.spring.dependence.demo1.ObjectA@4534b60d
可以看出把
构造函数去掉,然后增加
set方法就可以实现循环依赖的问题了。
Spring 的构造函数循环依赖
测试例子如下:
@Component public class MyBeanOne { private MyBeanTwo myBeanTwo; @Autowired public MyBeanOne(MyBeanTwo myBeanTwo) { this.myBeanTwo = myBeanTwo; } } @Component public class MyBeanTwo { private MyBeanOne myBeanOne; @Autowired public MyBeanTwo(MyBeanOne myBeanOne) { this.myBeanOne = myBeanOne; } } public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyBeanOne.class, MyBeanTwo.class); MyBeanOne myBeanOne = context.getBean(MyBeanOne.class); MyBeanTwo myBeanTwo = context.getBean(MyBeanTwo.class); System.out.println(myBeanOne); System.out.println(myBeanTwo); } }
输出如下:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'myBeanOne': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227)
在
DefaultSingletonBeanRegistry.beforeSingletonCreation()里打断点,看一下为什么出错了: 第一次经过: 第二次经过: 第三次经过:
我们可以猜测出错的原因是这样的:
去生成myBeanOne,需要生成myBeanTwo 去生成myBeanTwo,需要生成myBeanOne 去生成myBeanOne,发现myBeanOne已经在创建中了
Spring 的Set方式循环依赖
@Component public class MyBeanOne { @Autowired @Getter private MyBeanTwo myBeanTwo; } @Component public class MyBeanTwo { @Autowired @Getter private MyBeanOne myBeanOne; } public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyBeanOne.class, MyBeanTwo.class); MyBeanOne myBeanOne = context.getBean(MyBeanOne.class); MyBeanTwo myBeanTwo = context.getBean(MyBeanTwo.class); System.out.println(myBeanOne + " " + myBeanOne.getMyBeanTwo()); System.out.println(myBeanTwo + " " + myBeanTwo.getMyBeanOne()); } }
输出:
cn.eagle.li.spring.dependence.demo3.MyBeanOne@3c407114 cn.eagle.li.spring.dependence.demo3.MyBeanTwo@35ef1869 cn.eagle.li.spring.dependence.demo3.MyBeanTwo@35ef1869 cn.eagle.li.spring.dependence.demo3.MyBeanOne@3c407114
可以看出通过set注入的方式是可以解决循环依赖问题的。
我们继续在
DefaultSingletonBeanRegistry.beforeSingletonCreation()里打断点,看一下set方式是怎么经过这里的: 发现只经过了两次,没有第三次,如果有第三次的话,也就抛异常了,所以只经过两次是正常的。
为什么构造函数有三次,而set方式有两次?
我们看一下
DefaultSingletonBeanRegistry.beforeSingletonCreation的调用链:
AbstractBeanFactory.doGetBean DefaultSingletonBeanRegistry.getSingleton DefaultSingletonBeanRegistry.beforeSingletonCreation
那我们就在
AbstractBeanFactory.doGetBean这里打断点,看一下set方式,为什么没有第三次 第一次: 第二次: 第三次:
我们可以看到第三次的
myBeanOne已经有值了,它就不会执行到
DefaultSingletonBeanRegistry.beforeSingletonCreation如果换成构造方式来调试的话,在第三次,
myBeanOne依旧是
null的,就会继续往下执行到
DefaultSingletonBeanRegistry.beforeSingletonCreation,然后就会抛错。
我们来看一下第三次的
myBeanOne是怎么获取的:
DefaultSingletonBeanRegistry.getSingleton方法如下: 可以看到是通过
this.singletonFactories.get(beanName)得到一个工厂,通过这个工厂可以创建出对应的
bean
我们再来看一下这些个工厂是什么时候被放进去的,
DefaultSingletonBeanRegistry.addSingletonFactory:
为什么通过构造函数注入的方式,没有提前放入一个工厂
再执行
AbstractAutowireCapableBeanFactory.createBeanInstance方法时
set方式会执行以下: 调用这个方法之后,回退到
AbstractAutowireCapableBeanFactory.doCreateBean()继续往下执行,会把工厂放进去。
构造函数的方式会执行以下: 调用这个方法,后面会继续获取
myBeanTwo,如下: 然后同理再获取
myBeanOne,就会抛异常了,它不会回退到
AbstractAutowireCapableBeanFactory.doCreateBean(),自然也不会把工厂放进去。
最后理一下
对于Set方式,当类构造好之后,会提前把生成这个类的工厂放到缓存中;而构造函数的方式,由于存在构造函数,必须在当下去获取依赖类,所以就没办法构造类,其实原理和刚开始举的Java的例子是一个道理。
参考
Spring 解决循环依赖必须要三级缓存吗? Spring循环依赖三级缓存是否可以去掉第三级缓存? Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗
相关文章推荐
- spring处理循环依赖
- Spring-循环依赖处理机制-20200510
- Spring对加载的bean之间循环依赖的处理
- Spring对循环依赖的处理
- Spring是怎么解决循环依赖的?
- 原来spring是这样处理循环依赖的
- Spring源码解析:循环依赖的探测与处理
- Spring处理循环依赖
- spring当中的循环依赖怎么解决
- 同学,Spring 是怎么解决循环依赖的?
- 同学,Spring 是怎么解决循环依赖的?
- 死磕Spring之IoC篇 - 单例 Bean 的循环依赖处理
- 再谈spring的循环依赖是怎么造成的?
- JAVA,循环依赖,Spring
- Spring中的循环依赖解决详解
- Spring源码解析之Bean创建的核心步骤,三级缓存解决循环依赖的原理分析
- spring boot启动时mybatis报循环依赖的错误(推荐)
- spring是怎么实现依赖注入的
- 【第三章】 DI 之 3.2 循环依赖 ——跟我学spring3
- IDEA new 菜单下没有XML Configuration File,有了XML Configuration File没有Spring.xml,怎么办(加依赖)!