Spring如何解决循环依赖?
springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)
1 | java复制代码@component |
类A依赖了B作为属性,类B又使用类A作为属性,彼此循环依赖。
源码理解:
1 | java复制代码//调用AbstractBeanFactory.doGetBean(),向IOC容器获取Bean,触发依赖注入的方法 |
也就是实例化A的时候在缓存中没找到*[第一个getSingleton],就去第二个getSingleton实例化A[实际上是调用了doCreateBean()]*,由于A需要B,又去doGetBean尝试获取B,发现B也不在缓存中,继续调用第二个getSingleton去实例化,当要注入属性A的时候在二级缓存找到了半成品A,成功注入返回到A实例化的阶段,将B注入。
第一个getSingleton代码
1 | java复制代码 @Nullable |
在实例化AB的时候,三个缓存都是找不到这两个类的,因为两者均未创建;
三级缓存
1 | java复制代码 /** Cache of singleton objects: bean name --> bean instance */ |
第二个getSingleton代码
此处将A先创建好放入三级缓存中,实际上是委托给另一个**doGetBean()**完成的
1 | java复制代码public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { |
由于第一个获取单例的方法找不到AB,故此将会进入第二个获取单例的方法试图找到,这个方法里singletonFactory.getObject()为核心,将会回调到doCreateBean方法继续创建Bean。
doCreateBean代码
1 | java复制代码protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) |
在这个方法里,实例化A的时候,已经把A作为一个半成品通过调用addSingletonFactory方法将其加入了三级缓存singletonFactories,方便在递归实例化B的时候可以获取到A的半成品实例,详细代码如下:
将创建的bean加入三级缓存
发生在addSingletonFactory这个方法
1 | java复制代码protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { |
那么是在什么时候会发生一个递归的调用呢?
实际上是在populateBean(beanName, mbd, instanceWrapper);要做属性注入的时候,假设是根据名称自动注入的,调用autowireByName(),该法会去循环遍历在getBean之前已经把xml文件的属性加入到注册表之类的属性,
populateBean有一个autowireByName的方法,代码如下
1 | java复制代码protected void autowireByName( |
Object bean = getBean(propertyName); 这个方法实际上又去委托了doGetBean(),又一次递归的走上了流程,也就是A在实例化到该步时发现,还有一个B,就会又从doGetBean()开始,一步步的寻找创建,不同的是,当B走到这个根据名称注入的方法时,此时的已经能在二级缓存里找到A的身影了,无需再次创建A对象。
总结
spring运用三级缓存解决了循环依赖的问题;
采用递归的方式,逐步的去实例化对象,并将上一步已经加入缓存的半成品对象作为属性注入;
等到走到最后一个递归时,将会逐步返回,把对应的实例一个个创建好。
springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)
本文转载自: 掘金