Spring使用多级缓存解决循环依赖的问题

 在Spring的依赖中有一个比较特殊的依赖,它比较复杂而且重要,今天就来聊一下Spring中的依赖----循环依赖。
 循环依赖实际上是一种问题,他的描述就是一个A对象依赖了B对象,而B对象又依赖了A对象,所以这样产生了循环依赖这个问题,在Spring中会又三种循环依赖的情况,他们分别是两个对象中的构造方法中产生了依赖关系,另外一种情况就是单例模式下的set方法的循环依赖关系,第三种情况就是多例原型模式下的循环依赖问题,但是对于spring框架而言它只能够解决第二种情况,就是单例模式下的set方法的循环依赖问题。
 接下来我们来分析下spring是如何来解决循环依赖的,spring中主要是采用了三级缓存的一种模式来解决循环依赖,三级缓存听上去比较高大上,其实就是三个缓存的map结构。他们分别是singletonObjects,singletonFactories,earlySingletonObjects。
在这里插入图片描述
singletonObjects在spring中就是我们的容器在微观上的体现,最终我们所有生成的单例bean都会缓存在这个单例缓存池中,这是我们spring容器的核心对象,singletonFactories这个容器主要是存放了bean的早期对象,就是bean刚刚被实例化以后会放入其中,earlySingletonObjects这个对象主要是在填充循环依赖的属性的时候会做一个类型判断使用。
 介绍完了三种缓存map咱们接下来介绍下这三个map是如何在容器中使用的,我们的容器的bean在进行实例化的时候都会先去getBean然后如果容器中没有就会创建bean,实例化开始的方法是在applicationcontext中的refresh方法中的org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons在这个preInstantiateSingletons他会调用org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)然后再调用doGetBean方法org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean在这个doGetBean会首先调用一个getSingleton(beanName)这个方法,在这个方法中会尝试去单例池中(singletonObjects)中去获取,获取不到会到earlySingletonObjects中获取,如果还是获取不到就到singletonFactories中去获取,最终如果都没有合适的就返回null,如图:
在这里插入图片描述
当第一个类进来获取的时候肯定是获取不到的,所以此时返回为null。
如果是循环依赖还要判断是不是原型的,如果是原型的就会抛出异常,所以在这个地方有一个判断:
在这里插入图片描述
然后再判断当前容器是否有父类工厂,有的话就由父类工厂加载,如图:
在这里插入图片描述
接下来就是合并bean的定义,检查当前bean是不是抽象的,因为可能会有在xml中配置了两个类有继承关系这样需要合并其bean的定义:
在这里插入图片描述
然后就是做dependOn的检查:
在这里插入图片描述
在这个地方值得注意的是如果dependOn方法中出现了相互依赖的情况也会抛出异常。接下类最为重要的代码就是另外一个重载的getSingleton方法的调用,在这个方法中有一个方法叫做beforeSingletonCreation在调用这个方法的时候会将该类放在一个表示正在创建的list中如图:
在这里插入图片描述
这个标志至关重要,后期会用他来做判断,然后它会调用形参传入的singletonFactory的getObject方法如图:
在这里插入图片描述
在sonletonFactory的getObject方法中调用了createBean方法如图:
在这里插入图片描述
我们的目光接下来放到creatBean当中,在这个方法中会调用我们的resolveBeforeInstantiation方法来为aop寻找切面,最后再调用doCreateBean方法,如图:
在这里插入图片描述
在doGetBean中,这里是真正的创建bean的过程,在这里会根据你的参数来做一些判断看是选取无参构造还是有参构造,创建的时候我们把它当作黑盒不去管细节实现就可以,我们跟踪主要流程,接下来有一段特别重要的逻辑如图:
在这里插入图片描述
此处就是实现循环依赖的关键—提前暴露早期对象,当前bean是单例且允许循环依赖,且正在创建,所以最终会调用一个addSingletonFactory这个方法在这个方法中会再一次对三级缓存的内容做操作:
在这里插入图片描述
向singletonFactories放置了当前的bean,相当于提前暴露了bean这就是关键,然后从earlySingletonObjects移除bean。当程序执行到populateBean的时候,会对当前bean的属性进行赋值,此时发现需要另外一个bean,然后同样的流程去获取另外一个bean,姑且称之为B,B在实例化的时候也会走和之前的bean同样的流程,同样到最后执行到populateBean会为B填充属性,此时会发现他依赖之前的A,这时候就会去获取A,当获取A的时候就不一样了,这是后提前暴露的早期对象的作用就体现出来了在执行到第一个getSingleton的时候会去singletonFactories获取A,这个A在一开始初始化的时候就暴露了,所以此时能够获取到,这时候earlySingletonObjects也会把A放进去,然后第三级缓存singletonFactories就会去溢出这个bean具体代码如下:
在这里插入图片描述
这时就不会返回null了,不为null以后就会走接下来的逻辑:
在这里插入图片描述
在方法结尾会返回这个生成的bean对象这样B对象就开始填充A属性了,然后此时earlySingletonObjects的作用就体现了,在populateBean中会使用后置处理器进行依赖的注入。如下:
在这里插入图片描述
在这里边,会执行org.springframework.beans.factory.annotation.InjectionMetadata#inject这个方法,最终会执行如下红框的方法:
在这里插入图片描述
在红框的方法中会执行如下方法进行一个类型的匹配校验:
在这里插入图片描述
在这里插入图片描述
在一个空壳方法以后最终会来到isTypeMatch方法体中:
在这里插入图片描述
我们此时发现又调用了一个getSinleton方法,这就是我们一开始调用的方法但是此时不同的是获取的bean对象会从earlySingletonObjects中拿出来,做一下类型的匹配,意思就是当前的要注入的对象是不是之前的依赖对象。
 至此spring利用三级缓存解决setter循环依赖的问题就解决了,当然文章中还有很多方法使用了黑盒的理念,直接略过了我们只抓了主线,有什么不足之处望大神指教。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值