1,什么是循环依赖
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对象,最终形成闭环。比如A依赖于B,B依赖于A
2,怎么解决循环依赖问题
需要设置属性值,构造器无法解决循环依赖,只有set方法可以解决——(提前暴露对象)
对象在创建的过程中,是把实例化和初始化分开的,那么意味着中间可能存在一种状态,完成实例化但未完成初始化的状态。因为spring默认是单例的,把上述状态的对象拿出来放到某个空间,或者放到集合中(Map)(提前暴露对象)。后面可以通过当前集合对象的引用设置具体的属性值
结论:对象创建过程中,实例化和初始化是分开进行的,互不干扰。
DefaultSingletonBeanRegistry.java
/** Cache of singleton objects: bean name to bean instance. 一级缓存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. 三级缓存 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. 二级缓存*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
3,三个缓存中存放的对象有什么区别
- 一级缓存中放的是完整对象
- 二级缓存中放的是半成品对象(完成了实例化但是未完成初始化)
- 三级缓存中放的是lambda表达式对象
4,缓存中的对象是如何移动的
A : 三级缓存 ——> 二级缓存 ——> 一级缓存
B:三级缓存 ——> 一级缓存
5,能否直接使用一级缓存来解决循环依赖问题
一级缓存和二级缓存存放的是不同类型的对象,如果只有一级缓存,那么成品和半成品都会放到一起,在调用过程中,可能会获取到半成品对象
6,二级缓存能否解决循环依赖问题
此时,看起来是可以解决的。如果在循环依赖的过程中,不包含aop动态代理的话, 那么直接使用二级缓存足以解决问题,但当出现了aop动态代理之后,必须要使用三级缓存
7,为什么使用三级缓存
如果一个对象需要对代理,那么会通过反射的方式生成普通对象吗?通过反射的方式创建的普通对象一定会创建,只不过当需要动态代理的时候,会完成当前对象的替换工作, 在整个容器中,可能存在普通对象和代理对象,但是暴露给别人使用的一定是最终版本的对象(要么是普通对象,要么是代理对象)。
8,为什么使用lambda表达式就可以解决这个问题?
回调机制,当需要给外部暴露对象的时候,使用lambda表达式的回调方式能够最终返回出一个唯一的最终版本的对象,而我们在使用过程中,是无法判断或者设置什么时候被调用的,所以当第一次被调用的时候直接通过lambda表达式生成即可。