什么是循环依赖?
假设现在有一个对象A里面有一个属性Class B,同样的Class B对象中有一个Class A 的对象属性,那么这两个对象能相互创建成功吗?
可能一般的普通代码来说肯定是可以实现
A a = new A()
B b = new B()
a.setB(b)
b.setA (a)
看过之前讲的IOC的同学应该知道Spring官方是推荐使用构造器注入的,所以如果是通过构造器注入那就会产生一个无限循环注入的问题了,如下图所示,永远出来不?
A a = new A( new B( new A(new B(......))))
所以面试过程中的循环依赖问题其实都是问Setter
方式内部如何解决循环依赖的?而不是问的构造器。
比较初级的回答可能会说 是通过三层缓存,再好一点的回加上 三层缓存加上 提前暴露对象的方式(半成品)解决循环依赖问题
那什么是提前暴露对象呢?说白了就是spring IOC 容器的启动过程 bean 的整个生命周期过程处理的逻辑。之前跟大家聊SpringIOC的过程已经跟大家详细分享过了,就不再啰嗦了,还不了解的可以再去复习一下。
这里就直接再画一个流程图,大家针对这个图做一下回归复习
上面的这张图其实就是给大家说明了我们创建对象的时候可以分为两个大步骤,一个实例化,一个初始化。
同样的现在接着回到上面的问题,Setter是在哪一步处理缓存依赖的呢?
回顾整个流程我们大致可以按照这个思路来:
一个对象的创建 -> 实例化 -> 初始化(设置属性值)
那构造器的那种方式在流程中怎么体现出这个环呢?给大家画了一个图如下:
springIOC容器中的bean默认都是单例的,这个大家应该清楚的。所以在设置属性的时候可以直接在容器中获取,按照上面的创建流程那整个循环依赖就产生了。
三层缓存依赖,其实就是先把实例化的对象,放置在缓存中,等后续在根据A对象的引用完成赋值操作。
处理完的流程就是如下所示了:
在改进的图中其实已经可以发现,环 已经被打开了。整个可以如下几步:
在实例化A对象之后就向容器中添加一个缓存,存放一个实例化但未初始化完成的对象(半成品对象)。
在第一次创建A对象中容器已经有一个A对象,但是没有B对象,所以在开始创建B对象时,在完成B对象的实例化之后,开始初始化属性赋值时,此时容器中已经有A对象,所以可以直接通过A的属性赋值,同样的B对象完成初始化之后也就可以再接着完成初始化A对象了,那整个A对象和B对象的创建过程就完成了。
总结:
一级二级 三级缓存中分别存放的是什么状态的对象?
完整的看完这个文章的同学应该是没啥问题吧
一级:完整的成品的对象
二级:非完整的半成品对象
三级:lambada表达式
假设只设计二级缓存能否解决循环依赖?
只用二级缓存是可以解决缓存依赖的,(废弃第三级,保留第一第二)但是会有一个问题,在配置AOP切面的时候会出错,因为无法生成代理对象。
所以三级缓存是为了处理AOP中的循环依赖。因为当配置了切面之后,在getEarlyBeanReference方法中,有可能会把之前的原始对象替换成代理对象,导致Bean的版本不是最终的版本,所以报错。