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的例子是一个道理。
看完如果有帮助,希望可以给个 三连 ,你的鼓励就是我不断前进的动力。谢谢
关注我:私信获取Java高级架构资料、大厂面试试题、视频