极简介绍spring的循环依赖解决方案

// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// 三级缓存 
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);

看时先抛开spring的源码实现,对于未掌握的人可能会困于源码海而迷糊了。

就拿最经典的 A依赖于B,而B又依赖于A 为例

class A{B b;}

class B{A a;}

极简处理流程梳理如下:

1、假定应用启动的时候spring先扫描到A类,要将A类加进spring容器中,我们知道spring容器追到最底层,其实就是一个map,key为 beanName,value为对象实例。

2、没有循环依赖的情况就是将所有bean都放到这个map中。要用的时候直接map.get(beanName)就可以了。

3、有循环依赖的情况就要特殊处理了,假定还是只有一个map(一级缓存),在map.put(A)时,A必须是一个spring生命周期完整的bean。(生命周期完整是指:经过new A,对A进行属性赋值、对A进行init-method初始化等)

4、在对A进行属性赋值时,发觉A依赖于B,进而去创建B,转而又发现B又依赖于A。这时在B中想通过map.get(A)获取A时获取不了。而在A中想map.get(B)也获取不了,两者互相等待形成了死锁。

5、解决方案是启用二级缓存,亦即两个map。第一个map是完整可用的map。第二个map是存放实例化但还没赋值的不完整map

6、首先会将涉及循环依赖的beanName存到一个set中,回到bean创建时发觉在set中,那就启用二级缓存,回到第4步,将实例化后的Aput到二级缓存,转而B创建要A时,二级缓存的map get(A)直接拿到一个未完整的A赋值给B,完成B的创建进而让A也能完成创建。

7、至此普通的sping bean创建循环依赖问题就解决了。但为何spring最后还加多了一个map做三级缓存呢?

8、由于AOP代理的原因,spring bean被AOP动态代理了,会存在map.get(A)每次调用返回不同的实例。啥意思?Map<String, ObjectFactory<?>> singletonFactories。map.get(A)返回的都是ObjectFactory是同一个对象实例没错。但真正的bean还要ObjectFactory.get()。如果是AOP代理的bean。同一个ObjectFactory,每次调用get()都返回不同的实例。

PS :这里可以自已测试下:在DefaultSingletonBeanRegistry类中的getSingelton方法:

9、如果只是二级缓存,AOP代理会导致在创建A时map.get(A)拿到的ObjectFactory再get()出来的A实例地址 与创建B时装配A时map.get(A)再get()得到的A的实例地址不一致

10、为了处理这个问题加多一个map在Aget(A)后直接将数据给这个map。避免在Bget(A)时拿的实例地址不一致的问题。

至此整个spring 循环依赖的大体流程就算结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值