三级缓存的数据结构
- 三个级别的缓存都是Map结构
- 查找顺序会先从一级缓存开始找,没找到会走二级缓存,最后查三级缓存
- 技巧:三级缓存中保存的都是没有被查找过的对象,因为查找到三级缓存一定会调用getSingleton()方法, 而调用这个方法的时候就会将其移动到二级缓存。注意: 三级缓存中保存的都是没被查找过的对象,但是没被查找过的对应不一定都在三级缓存里,也有可能在一级缓存
三级缓存的作用
- 一级缓存SingletonObjects: 存放完整可直接提供给外部使用的对象,完全成熟
- 二级缓存EarlySingletonObjects: 存放还未填充属性的对象,可以认为只是个空壳子,主要作用解决循环依赖
- 三级缓存SingletonFactories: 工厂刚出炉的对象,可以认为还处于在工厂中的阶段,依然是个空壳子, 调用完构造方法后,就会将对象放到三级缓存,主要作用解决循环依赖
对象何时从三级缓存移动到二级缓存,与一级缓存的?
- 当调用getSingleton()方法的时候,会移动到二级缓存,此时二级缓存会add(), 而三级缓存会调用remove()。
- 时机:当A,B两个对象相互依赖,A对象实例化完毕后会放到3级缓存,此时依赖注入需要立即创建B对象,B对象注入A对象时,就会从三级缓存找到A,此时会调用getSingleton(), 也是这个时候,A对象会从三级缓存移出进入到二级缓存。
- 当对象已竟完整的被创建后,会add()到一级缓存,然后remove()掉二级和三级缓存
扩展: Spring如何解决循环依赖的流程
- 前提: A,B两个对象相互依赖。
- 创建A对象时,实例化完毕,即调用构造方法完毕时会将A这个不成熟的空壳子对象放到三级缓存中
- 紧接着会依赖注入,此时发现自己需要B对象,所以会从一级缓存开始找B对象,直至找到第三级缓存,此时B对象肯定不在三级缓存中。
- 没找到B对象会立即去创建B对象,创建B对象时依然会走同样的创建流程,先实例化,然后依赖注入。
- 当B对象进行依赖注入时,发现自己需要A对象,所以会调用getSingleton()方法,从一级缓存开始找A。当然最终会在三级缓存中找到,因为A实例化后就放到三级缓存了。
- B在三级缓存找到A对象后,会将A对象添加到二级缓存,然后remove()掉三级缓存里面的A对象。
- 至此为止,B完成了依赖注入,然后开始初始化,初始化完毕后会将B对象放到一级缓存,然后remove()掉二级缓存里面的B对象,B对象此时创建完成。
- 回头再重新对A对象进行依赖注入,此时A对象从一级缓存就能直接找到B对象进行依赖注入了,然后A开始初始化,最终完成bean创建
扩展2: 什么样的循环依赖Spring无法解决?
- 循环依赖在构造方法中,此时Spring无法解决这类循环依赖,因为创建bean依靠的就是无参构造方法。换句大白话就是A还没进入三级缓存,就要产生了循环依赖,这就完了
- 多例的场景下无法解决循环依赖,三级缓存解决的场景一定是单例情况下。