Spring循环依赖
条评论前置知识
- Spring的依赖注入方式,我们分为setter注入和构造器注入。那么Spring可以解决掉setter类型的依赖注入,构造器形式的是不可以的。
- Spring的单例和多例模式,多例模式下的依赖注入也是解决不掉的。所以我们关注的范围为单例模式下setter注入形式的循环依赖的解决方案。
- SpringBean的生命周期我们可以概括为
实例化、属性赋值、初始化、销毁四个大的阶段。
循环依赖的场景
1 |
|
类似上面这样,A中有一个属性引用了B,B中有一个属性引用了A。那么这就是经典的循环依赖,形成一个圈,拓展到多个Bean,也可以是A依赖B,B依赖C,C依赖A。这样也是循环依赖的场景。
再来好好理解一下上面说的生命周期,实例化A的时候,我们去属性赋值B。于是去找到B,B又进行实例化,B属性赋值A。于是A又找B,于是产生了死循环。
Spring是怎么解决的
引入三级缓存
其实是三个Map
1 |
|
一级缓存
假设我们没有三级缓存,只有一级缓存,那我们会怎么样进行处理呢?
1 | 首先A对象进行实例化,A要进行属性填充B。但是B还没有被创建,于是开始B进行实例化, |
那么有人会说,我的A不进行属性填充,直接丢一级缓存里面,那不就可以了吗?
这样就会造成map里面存的A是一个缺胳膊少腿的A,当真正用它的时候,会报空指针异常。而且我们的一级缓存规定存放的是完全初始化好的Bean。所以大家应该理解了,一级缓存是行不通的,于是Spring的大神想到了二级缓存。
二级缓存
上面说到一级缓存的问题在于没法存储半成品的对象,也就是未完全初始化好的对象,那么搞一个二级缓存专门存储这个。
下面这段话,我把一级缓存叫做map1,二级缓存叫做map2。
1 | 从实例化A开始,实例化之后还没有进行属性填充的时候,就把A`对象的引用`放入到map2中备用。 |
三级缓存
按照上面的分析,二级缓存已经就可以解决循环依赖的问题了,那么为什么还搞出来一个三级缓存呢?
这主要是因为Spring的Aop机制所产生的代理对象问题。
首先要了解到一个前置是Spring的代理对象产生的阶段是在填充属性之后才产生的,原理通过后置处理器BeanPostProcessor来实现的。如果说我们用二级缓存来解决,那么就要在属性填充的时候,将代理对象生成好,放入二级缓存。那么就与我们Spring的对象生命周期相悖。所以这个方式不好,于是有了三级缓存。
三级缓存主要针对的是动态代理类型的对象,通过工厂在真正需要动态代理对象的时候才来获取,这样可以提升一些性能,因为创建动态代理对象是一个消耗比较大的过程。
1 | 首先,还是进行实例化A对象,这个时候要将A的ObjectFactory对象放入map3中。 |
循环依赖问题就此解决。
文章作者:米兰
原始链接:https://blog.milanchen.site/posts/spring-circular-dependency.html
版权声明:转载请声明出处