(面试的时候可能会被问到哟!!!!!!!)
1.定义
循环依赖就是循环引用,就是两个或者两个以上的bean对象互相持有对方,最终形成闭环。
比如:A依赖B,B又依赖C,C又依赖A,形成循环依赖。
2.出现的场景
构造器的循环依赖
field属性循环依赖
3.如何检测循环依赖
在创建bean的时候可以给该bean打个标签,如果递归调用回来发现bean正在创建的话,就表示发生了循环依赖。
4.如何解决循环依赖
Spring内部通过三级缓存来解决循环依赖的问题。
所谓三级缓存,就是spring容器解决循环依赖的三个map:
一级缓存:singletonObjects,是一个ConcurrentHashMap,也叫单列池,存放已经经历了完整生命周期的Bean对象;
二级缓存:earlySingletonObjects,是一个HashMap,存放早期暴露出来的Bean对象,Bean的生命周期未结束,属性还未填充完整;
三级缓存:singletonFactories,第三级缓存value是一个工厂,三级缓存存放可以生成bean的工厂。
注意:
只有单例的Bean会通过三级缓存来解决循环依赖的问题,而非单例的Bean,每次从容器获取都是一个新的对象,都会重新创建,所以非单例的Bean是没有缓存的,不会放在三级缓存中。
整个流程大致如下:
1.
首先
A
完成初始化第一步并将自己
提前曝光
出来(通过
ObjectFactory
将自己提前曝光),在
初始化的时候,发现自己依赖对象
B
,此时就会去尝试
get(B)
,这个时候发现
B
还没有被创建
出来;
2.
然后
B
就走创建流程,在
B
初始化的时候,同样发现自己依赖
C
,
C
也没有被创建出来;
3.
这个时候
C
又开始初始化进程,但是在初始化的过程中发现自己依赖
A
,于是尝试
get(A)
。这
个时候由于
A
已经添加至缓存中(一般都是添加至三级缓存
singletonFactories
),通过 ObjectFactory 提前曝光,所以可以通过
ObjectFactory#getObject()
方法来拿到
A
对象。
C
拿
到
A
对象后顺利完成初始化,然后将自己添加到一级缓存中;
4.
回到
B
,
B
也可以拿到
C
对象,完成初始化,
A
可以顺利拿到
B
完成初始化。到这里整个链路
就已经完成了初始化过程了。
关键字:三级缓存,提前暴露