Spring循环依赖及其三级缓存

Spring在正常创建Bean对象的过程中,会先去创建该对象中的属性,比如A对象正在创建,会先去创建A中的属性B对应的Bean对象,这种情况下,如果B中所对应的Bean对象也存在A属性,就会造成互相创建对象的情况,这种情况Spring称之为循环依赖,A->B,B->A。对于这种最常见的场景,只需要在中间增加一个缓存既可以解决,解决方法如下

可以看到,如果仅仅针对这种情况只需要在中间加一层缓存来解决即可,加上单例池,总共也就二级缓存,但是Spring为什么需要提供三级缓存来解决其中的循环依赖?

        Spring中存在要针对某些点进行额外的逻辑注入,创建代理对象的情况(Aop),通常这种情况会在初始化(AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization)中执行,会拿出其所有的Advisor然后根据匹配规则去逐个匹配得到增强链。

        如果一个对象需要进行Aop的代理增强,那么他在单例池中存的就是代理对象,而非一开始就创建出来的原始对象,如果是这样,那么我们会在创建B的过程中因为得到了A的原始对象(但是最后存于单例池中的却是代理对象)而产生冲突,所以我们需要再做一个缓存,针对于这种即产生循环依赖又产生Aop动态代理的特殊情况打破原有的Bean生命周期。即把Aop提前进行,在循环依赖产生的时候提前进行Aop。

具体细节可以看一下Spring解决循环依赖的代码

isSingleTonCurrentlyIncreation方法会去判断在创建Bean的过程中是否存在循环依赖的情况,创建A的时候会记录A在一个singletonsCurrentlyInCreation (HashSet)中,接着去创建A中的属性时候会用this.singletonsCurrentlyInCreation.contains(beanName);去判断是否产生了互相引用的情况,如果存在这种情况那么会在addSingletonFactory方法中传入lambda表达式。这个lambda表达式只有在出现了循环依赖的情况下才会执行

并且会在AbstractAutoProxyCreator.wrapIfNecessary方法中执行此lambda表达式,该lambda表达式会去调用getAdvicesAndAdvisorsForBean方法来判断是否要进行代理对象的生成。如果的确需要生成代理对象,那么其实还会记录到另一个缓存(earlyProxyReferences)来表示该对象是进行过Aop了,后续不需要再执行额外的操作,所以在某种角度也可以称之为四级缓存。如果不需要进行代理,那么还是会将Bean原样返回,并不需要进行额外的操作,反之则会将proxy返回

getAdvicesAndAdvisorsForBean->findEligibleAdvisors->findAdvisorsThatCanApply->AopUtils.canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions),该方法会最后去判断是否要进行Aop,会先找出所有的Advisor,然后根据Advisor的匹配规则(ClassFilter和MethodMatcher),先进进行类的匹配,然后再进行类下的方法匹配来判断是否需要对当前Bean进行动态代理。


执行完Lambda表达式之后便会将其放入到二级缓存(earlySingletonObjects)中,然后去完成B对象的创建,最后再完成A对象创建之后将其设置到一级缓存singletonObjects中

下面代码是解决循环依赖的核心逻辑,先从singletonObjects(一级缓存)取,如果取不到就从earlySingletonObjects(二级缓存)取,再然后取不到,就从singletonFactories(三级缓存)中取出一个lambda表达式然后再执行它得到一个对象。

总结,其实大致可以分为四个缓存类,只不过在getSIngleton方法中我们只用到了三个

singletonObjects :缓存经过了 完整生命周期 的bean
earlySingletonObjects:已经实例化完成但是尚未注入属性的bean,里面会存放代理对象或者非代理对象,这块逻辑在 AbstractAutoProxyCreator.wrapIfNecessary中执行,会判断到底需要返回什么对象
singletonFactories:缓存一个lambda表达式, 经过 实例化 得到一个原始对象后,都会提前基于原始对象暴露一个lambda表达 式,并保存到三级缓存中,这个lambda表达式 可能用到,也可能用不到 ,如果当前Bean没有出 现循环依赖,那么这个Lambda表达式没用
earlyProxyReferences:用来该Bean对象是否提前进行过Aop

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值