八字真言:“三级缓存,提前暴露”
此文只是介绍简单的情况便于理解,实际上场景会更复杂、情况会更多,但是原理相通。一、什么是循环依赖?
从字面上来理解就是A依赖B的同时B也依赖了A,就像下面这样 上图是简单的循环依赖,也会存在A依赖B,B依赖C,C依赖A这种循环,或者更复杂的情况。 (在实际工作中应该尽量避免出现循环依赖的情况)二、什么情况下循环依赖可以被处理?
三、Spring是如何解决的循环依赖?
三级缓存:
一级缓存 : Map<string,object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例 二级缓存 : Map<string,object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例 三级缓存 : Map<string,objectfactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。流程图:
总结:
假如testService1依赖testService2,testService2依赖testService1 Bean初始化流程:- 创建 testService1实例,实例化的时候把 testService1 对象⼯⼚放⼊三级缓存,并提前先暴露出来;
- testService1 注⼊属性时,发现依赖 testService2,此时 testService2 还没有被创建出来,所以去实例化 testService2;
- 同样,testService2注⼊属性时发现依赖 testService1,它就会从缓存里找 testService1对象。依次从⼀级到三级缓存查询 testService1,从三级缓存通过对象⼯⼚拿到 testService1,发现 testService1虽然不太完善,但是存在,把 testService1 放⼊⼆级缓存,同时删除三级缓存中的 testService1,此时,testService2 已经实例化并且初始化完成,把testService2放入⼀级缓存
- 接着testService1继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的testService2对象,testService1对象创建也完成,删除⼆级缓存中的 testService1,同时把testService1放⼊⼀级缓存
- 最后,⼀级缓存中保存着实例化、初始化都完成的 testService1、testService2 对象