首页>>后端>>Spring->Spring循环依赖详解

Spring循环依赖详解

时间:2023-11-30 本站 点击:1

什么是循环依赖

所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。它们之间的依赖关系如下:

spring容器最大的功能就是对bean的生命周期进行管理,每个bean在创建的过程中,需要得到一个完整的bean需要对bean的所有属性进行赋值,如果两个bean出现了相互依赖的情况,那么如果spring没有处理循环依赖,那么出现的结果就是在bean的创建过程中出现相互依赖,导致这个bean永远无法创建出来,则就导致一直在相互创建,最终出现的结果就是出现Stack Overflow异常,也就是栈溢出,类似于一个递归一样的。 spring在创建bean的时候,过程如下:

实例化前;

实例化bean(推断构造);

实例化后;

填充属性;

初始化前;

初始后; 上面的这些过程在第4步就会对bean进行属性填充,也就是依赖注入,依赖注入就会将bean中的所有属性进行填充,填充的时候如果发现了循环依赖,那么就会进行循环依赖的处理,如果spring没有处理循环依赖,那么出现的结果如下: 这样就会出现了一个闭环,程序用于无法结束,就会出现Stack Overflow异常。

spring是如何解决循环依赖

spring 的bean在创建bean的过程中会涉及到几个集合

singltonOjects:一级缓存(单例池)

earlySingltonObjects:二级缓存

singltonFactories:三级缓存

singletonsCurrentlyInCreation:存放正在创建bean的set集合,存放的是正在创建的bean的名字

一级缓存:一级缓存就是我们常常说的spring的单例容器,spring依赖注入的bean都是从这个单例池中去获取的,创建后完整的bean也是存放在这个单例池中的,我们这里叫它为一级缓存; 二级缓存:二级缓存从名字上可以知道是叫做早期的单例对象,就是说它是存放的是早期的单例对象,可能是不完整的; 三级缓存:存放的是我们的实例化后的原始对象,就是存放一个刚刚被创建出来的原始对象。

首先我们的一个对象被创建出来过后会放入三级缓存中,移除二级缓存,三级缓存存放的是beanName对应的一个lambad表达式,这个表达式封装了刚刚实例化出来的对象、bean对应的BeanDefinition、beanName封装在一个ObjectFactory中,所以这个时候存放到三级缓存中的只是一个lambad表达式,当调用ObjectFactory的getObject的时候才会去执行lambad表达式,也就是方法的调用;

如果整个过程中没有出现循环依赖,那么永远不会使用到二级缓存,而且三级缓存也只是存放了实例化后封装的对象,也不会去使用;当出现了循环依赖的时候,那么这个时候就会启用二级和三级缓存,首先从三级缓存取出对象,然后执行getObject,将返回的对象(如果出现了循环依赖,而又开启了aop,返回的是一个代理对象)放入二级缓存,然后移除三级缓存;

到这里可以得到一个知识点就是二级缓存和三级缓存是一对儿存在的,就是一个对象要么在二级缓存,要么在三级缓存,他们两个是成对出现的,存入二级缓存的时候,移除三级缓存,存入三级缓存的时候,移除二级缓存。

@ComponentpublicclassAService{@AutowiredprivateBServicebS;}@ComponentpublicclassBService{@AutowiredprivateAServiceaS;}

上面这两个Bean是相互依赖的,也就是循环依赖的,加入spring首先扫描到是AService,那么AService的过程如下: AService创建过程:

实例化AService,得到AService的原始对象 aS,将实例化后的对象aS放入—>三级缓存

填充aS中的bS->去容器找bS->找不到->进入创建BService的流程—>将返回的bS对象复制给AService中的bS;

填充其他属性;

初始化bean;

放入一级缓存单例池。

移除bS在二级、三级缓存中的对象

BService创建过程:

实例化BService,得到BService的原始对象 bS,将实例化后的对象bS放入—>三级缓存;

填充bS中的aS->去容器找aS->判断as是否正在被创建—>从三级缓存获取aS—>调用三级缓存获取的lambad表达式(当出现了循环依赖,如果bean启用了循环依赖,这里调用lambad会执行aop,返回一个代理对象)—>将得到的对象放入二级缓存—>移除三级缓存–>返回A的实例对象–>赋值给BService 中的aS;

填充其他属性;

初始化bean;

放入一级缓存单例池(返回bS给Aservice的第二步)。

移除bS在二级、三级缓存中的对象。

当AService创建完成过后,然后去创建BService,发现BService已经被创建了,因为创建Aservice的时候出现了循环依赖,就已经去创建了BService,所以这样就处理完成了。

spring会什么要三个缓存,具体说是为什么需要二级和三级缓存

spring会什么要三个缓存二级缓存是存放了原始对象,而三级缓存是存放的是可以aop过后的对象或者改变过后的对象,我们上面说的是AService和BService相互依赖,但是如果还有一个CService依赖AService或者BService,而AService或者BService也依赖CService呢?

而且AService、BService、CService都是单例的,也就是这三个Bean都只能存在一份,只能实例化一次。

而三级缓存存放的是实例化后的原始对象封装的对象,如果循环依赖的对象开启了aop,那么这个时候如果没有三级缓存,那么是不是每次都要取出三级缓存中的原始对象进行aop,那么这样的话,最后放入一级缓存的对象就不是相同的,会被覆盖,那么前面赋值的对象会被覆盖,这样没有体现了单例的思想了,所以二级缓存是存放早期比较完整的对象,三级是存放刚刚实例化后的对象。 最后放入缓存的对象都是从二级缓存获取的,你就把三级和二级比喻成一个刚刚出生的小孩和准备走上社会的成年人,但是他们都是同一个对象,就算做了aop,那么aop中target还是它自己,从出生到结束都是它一个对象,除非在放入单例池的时候被后置处理器改变了。

一个循环依赖处理的大概流程:


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Spring/4490.html