1.循环依赖的概述
2.三级缓存的概述
3.循环依赖的案例分析
循环依赖:
循环依赖是指在Spring框架中创建Bean对象的过程中,多个实体类之间相互依赖,形成闭环。比如说有A,B,C三个实体类,其中A类引用了B类,B类引用了C类,而C类又引用了A类,形成了一个闭环,因此循环依赖,也被称为循环引用。
三级缓存:
概述:
三级缓存是Spring框架提供的三个Map集合,用于存储Bean对象在创建过程中形成的完整Bean对象和不完整的Bean对象。
一级缓存:
一级缓存是指用于存储最终完整的Bean对象的容器,即上图中的singletonObjects集合。此容器存储的Bean对象已完成了实例化和初始化,可通过ApplicationConText接口的getBean()方法,直接获取实例化的对象。
二级缓存:
二级缓存是指用于存储不完整的Bean对象的容器,即上图中的earlySingletonObjects集合,此容器中存储的Bean对象完成了实例化,但还没完成初始化,因此属于“半成品”。但在循环依赖中,由于类A对象在初始化过程中需要引用另一个暂时还不存在的类B对象,导致类A对象无法完成完整的初始化过程,产生一个完整的Bean对象,但又因为类A对象被它所引用的类B所引用,此时会将暂不完整的Bean对象A暂存放于二级缓存容器中,供类B对象初始化时引用。
三级缓存:
三级缓存指上图中的singletonFactories集合,此集合与二级缓存的功能类似,都是用来存放暂不完整的Bean对象的容器。但与二级缓存不同的是,三级缓存中存放的Bean对象没有被其他类所引用,只是单纯存放不完整Bean对象。
案例分析:
需求:
假设现在有两个实体类Dao和Service,这两个类分别依赖彼此。现有个需求:分别完成这两个类对象的实例化和初始化,获取两个完整的Bean对象存放在一级缓存集合中。
需求分析:
如果在没有引入三级缓存的情况下,在先加载的Bean对象(Service)完成实例化后开始初始化时,由于引用了类Dao,此时会在singletonObjects(一级缓存集合)中找寻类Dao的实例化对象。由于此时类Dao的Bean对象还未被加载,因此一级缓存中并没有Dao对象。此时Service对象的创建会被暂停,转而创建Dao对象。但由于Dao也引用了Service,在Dao对象初始化过程中也会出现上面的问题,进而会创建多个不完整的Bean对象,形成一个死循环。如下图所示:
优化方案:
引入三级缓存概念,Bean对象在初始化过程中由于引用另一个类对象而被迫暂停时,可暂时将此不完整的Bean对象存放到三级缓存中。进而在创建引用类对象时,由于此类对象存放于三级缓存中,引用类对象在从三级缓存到一级缓存的寻找过程中,可以找寻到此类对象,因而可完成引用类对象的完整创建,打破了上面的死循环,进而完成此类对象的完整创建。
实现步骤:
1.Service 实例化对象,但尚未初始化,将Service存储到三级缓存;
2.Service 属性注入,需要Dao,从缓存中获取,未获取到Dao;
3.Dao实例化对象,但尚未初始化,将Dao存储到到三级缓存;
4.Dao属性注入,需要Service,从三级缓存获取Service,获取到Service后将Service从三级缓存移入二级缓存;
5.Dao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,并从三级缓存中删除此对象;
6.Service 从一级缓存中找到Dao,完成对Dao的注入;
7.Service执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,并从二三级缓存中删除此对象。