对于循环依赖的解决,首先得了解Spring IOC 容器的创建过程,在加载过程中,Bean 的实例化和初始化是分开的,所以在解决循环依赖的问题时,也是基于Bean 的实例化和初始化分开执行这一特点。
-
我们将实例化后的Bean 叫 半成品,将初始化后的Bean 叫成品,成品的Bean 可以给外部使用,而半成品的Bean 不能给外部使用。我们在使用三级缓存来解决的时候,正好利用了这一点,将半成品的Bean 提前暴露。
以下是Spring 源码在的三级缓存结构:
-
三个缓存都是Map结构,而唯一不同的是二级缓存是Map结构的value 参数是个函数式接口ObjectFactory<?>, ObjectFactory<?>是一个函数式接口,仅有一个方法,可以传入lambda表达式,可以是匿名内部类,通i过调用getObject方法来执行具体的逻辑
-
三个缓存中分别放不同的value值
一级缓存放成品对象(实例化+初始化)
二级缓存存放半成品对象(实例化)
三级缓存存放对象的lambda 表达式 -
在3个缓存中进行对象查找时,依次是通过一级缓存,二级缓存,三级缓存
-
三级缓存中使用lambda 表达式的原因是:对象在什么时候被暴露出去或者被其他对象引用是没办法提前确定好的,所以只有在被调用的那刻才可以进行原始对象还是代理对象的判断,使用lambda表达式类似于一种回调机制,不暴露的时候不需要调用执行,当需要被调用的时候,才真正的执行lambda表达式,来判断返回的到底是代理对象还是原始对象
引申问题
-
如果只有一个map结构,能解决循环依赖问题吗?
理论上可行,实际上没人这么干,使用两个map的意义在于将成本对象和半成品对象进行区
分,半成品对象是不能直接暴露给外部对象使用的,可以设置标志位来标识成品还是半成品,
是操作起来比较麻烦,所以直接用两个map即可 -
如果只有两个map结构,能解决循环依赖问题吗?
可以解决,但是有前提:没有代理对象的时候,当不使用aop的时候,两个缓存map就可以解决
循环依赖问题 -
为什么使用三级缓存之后就可以解决带aop的循环引用?
《1》一个容器中,能包含同名的两个对象吗?
不能
《2》对象创建过程中,原始的对象有没有可能需要生成代理对象?
有
《3》如果创建出了代理对象,那么程序在调用的时候到底使用原始对象还是代理对象?
应该用代理对象,但是程序是死的,是提前写好的,他怎么知道要选择代理对象呢?
所以,当出现代理对象的时候,要使用代理对象替换掉原始对象
《4》代理对象的创建实在初始化过程的扩展阶段,而属性的赋值是在生成代理对象之前执行的
那怎么完成替换呢?
需要在属性赋值的时候判断是否需要生成代理对象
《5》那为什么非要使用lambda表达式的机制来完成呢?
对象在什么时候被暴露出去或者被其他对象引用是没办法提前确定好的,所以只有在被调用的那
刻才可以进行原始对象还是代理对象的判断,使用lambda表达式类似于一种回调机制,不暴露
的时候不需要调用执行,当需要被调用的时候,才真正的执行lambda表达式,来判断返回的到底
是代理对象还是原始对象