看到一篇文章讲Spring如何解决循环依赖问题,记录一下:
PS:其实正确答案是开发人员做好设计,别让Bean循环依赖,但是没办法,面试官不想听这个。
我们都知道,单例Bean初始化完成,要经历三步: 注入就发生在第二步,属性赋值,结合这个过程,Spring 通过三级缓存解决了循环依赖:
1、一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例 2、二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例 3、三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
我们来看一下三级缓存解决循环依赖的过程: 当 A、B 两个类发生循环依赖时: A实例的初始化过程:
1、创建A实例,实例化的时候把A对象??放?三级缓存,表示A开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道
2、A注?属性时,发现依赖B,此时B还没有被创建出来,所以去实例化B
3、同样,B注?属性时发现依赖A,它就会从缓存里找A对象。依次从?级到三级缓存查询A,从三级缓存通过对象??拿到A,发现A虽然不太完善,但是存在,把A放??级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入?级缓存。
4、接着A继续属性赋值,顺利从?级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除?级缓存中的A,同时把A放??级缓存
5、最后,?级缓存中保存着实例化、初始化都完成的A、B对象
所以,我们就知道为什么Spring能解决setter注入的循环依赖了,因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的化,那么都得在实例化这一步完成注入,所以自然是无法支持了。
为什么二级缓存不行,必须三级缓存:
主要是为了?成代理对象。如果是没有代理的情况下,使用二级缓存解决循环依赖也是OK的。但是如果存在代理,三级没有问题,二级就不行了。
因为三级缓存中放的是?成具体对象的匿名内部类,获取Object的时候,它可以?成代理对象,也可以返回普通对象。使?三级缓存主要是为了保证不管什么时候使?的都是?个对象。
假设只有?级缓存的情况,往?级缓存中放的显示?个普通的Bean对象,Bean初始化过程中,通过 BeanPostProcessor 去?成代理对象之后,覆盖掉?级缓存中的普通Bean对象,那么可能就导致取到的Bean对象不一致了。
参考文献:https://mp.weixin.qq.com/s/Y17S85ntHm_MLTZMJdtjQQ
|