目录
什么是Spring中的循环依赖问题?
注意这个问题与JVM中垃圾回收时的循环依赖问题进行区分。
JVM中垃圾回收时的循环依赖问题可以参考这篇博客中的垃圾回收部分。
Spring中的循环依赖问题是指IOC的加载过程当中bean创建的时候发生的,如下图所示,比如说当我们IOC加载创建BeanA的时候首先会去实例化A,然后进行属性注入解析@Autowired注解,发现依赖一个BeanB,这个时候会去IOC容器中帮我们去找BeanB,如果没有找到那么同样会先实例化B,然后进行属性注入解析@Autowired注解又发现依赖BeanA,这个时候又去IOC容器当中去拿BeanA,然后又没有拿到,又会去帮我们创建BeanA,就形成了一个闭环死循环。
答案:Spring则通过三级缓存来解决循环依赖问题。
循环典型的场景有以下三种:1.自我依赖。2.相互依赖。3.多个bean之间的依赖。比如A依赖B,B依赖C,…,而N又依赖A。
正常我们在创建Bean的时候完全可以通过先创建A和B两个对象的方法解决循环依赖,但是Spirng在创建非懒加载单例bean的时候出现循环依赖的问题是因为Spring的对象创建过程分为创建对象——实例化——属性填充三个过程。
什么是Spring中的三级缓存?
为了解决循环依赖问题,Spring 引入了三级缓存。那么,什么是 Spring 的三级缓存呢?首先,让我们看一下它们在DefaultSingletonBeanRegistry类中的定义,其实它们本质上就是一个Map。
—级缓存
一级缓存被命名为singletonObjects,也被称作单例池,它存储的是经历了完整的创建过程的单例 bean对象。一级缓存是一个ConcurrentHashMap,它的 key是String 类型,保存的是beanName; value 是Object 类型,保存的创建好的单例bean对象。
单例池并非专门用于解决Spring 的循环依赖问题,即便不考虑循环依赖,经历了Spring容器完整创建过程的单例 bean对象也会被放进该单例池。只不过在Spring解决循环依赖问题的三级缓存中被称为一级缓存。
单例池的主要作用是用来保证这些完全创建好的 bean是单例的。
二级缓存
二级缓存被命名为earlySingletonObjects,它存储的是尚未完全创建好的单例 bean对象。二级缓存的存储结构和一级缓存完全一样,只不过它们存储的对象有所不同,一个是已经创建好的完整的 bean对象;另外一个则是尚未创建好的单例 bean对象或者叫半成品对象。
在创建单例bean时,如果发现该bean存在循环依赖,则会提前把这个尚未完全创建好的半成品对象放入到二级缓存中。如果该bean需要进行AOP,则该半成品对象就是它的代理对象,否则就是它实例化之后但是尚未属性填充的原始对象。
二级缓存的主要作用是用来保证这些尚未完全创建好的半成品对象是单例的。
三级缓存
三级缓存被命名为singletonFactories,它存储的是尚未完全创建好的单例 bean对象的生成工厂。三级缓存是一个HashMap,它的key 是String类型,保存的是beanName; value 是ObjectFactory类型,保存的是生成尚未创建好的单例 bean的对象工厂。
可能有些小伙伴对ObjectFactory有点陌生,它其实是Spring 提供的一个函数式接口,既然是函数式接口,我们就可以使用Lambda 表达式进行实现,并且可以在任何我们需要结果的时候才真正去执行Lambda表达式。