Spring中Bean的循环依赖以及处理原理

Spring中Bean的循环依赖以及处理原理

什么是循环依赖

//下面是简单的Spring循环依赖示例
@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private B b; 
}

​ 以上代码如果在类上面没有加上@Component就是普通的java类,在加上这个注解后就会交给Spring来管理,那么此时这些类就不需要我么自己去创建了Spring会自动帮我们创建然后放到单例池里面去.说道这里大家肯定也都大致知道想要了解Spring处理循环依赖的原理肯定是和Bean的生命周期有关了,下面我们就来具体的看一下吧.

Bean的生命周期

​ 我这里不是具体的来分析Bean的生命周期,在Spring中Bean的生命还是有很多步骤的.为了探索Spring中Bean的循环依赖我们就只分析一下主要的步骤,下面来列一下最简单的生命周期吧.

class—>Beandifinition—>实例化—>填充自动注入属性—>填充其他属性—>AOP—>单例池

当然了还有一些Beandifition的后置处理器,Aware回调之类的这里就先不考虑了.从上面的分析来看我们不难看出在第三步的时候Spring会去填充自动注入的属性,这个时候就先去单例池找B的bean这个时候肯定是找不到那么就需要去创建B然后在创建B的时候又会再走一次Bean的生命周期,这个时候发现需要注入A这个时候又去单例池找A的bean肯定也是找不到的又会去创建A…如此循环下去,如下图

在这里插入图片描述

如何打破这个循环

​ 首先这个循环是一个闭环想要打破肯定是需要去添加一些步骤的,我们可以一下能不能在实例化A对象的时候将A对象存入一个Map集合里面呢?

​ 在实例化A的时候我们肯定会得到一个对象的注意啊这个还不是bean,将这个对象存入到Map中在需要的时候再去取出来用这个时候是不是就可以不用去再实例化一个对象了呢?那么再加一不我们再看一下.

class—>Beandifinition—>实例化----->暂时存入一个Map<beanName,原始对象>----->填充自动注入属性—>填充其他属性—>AOP—>单例池.

下面我们再来看一下是否能打破这个循环呢?在A实例化的时候将A对象存入Map中,然后去注入B发现B需要A这个时候我们就可以去Map里面取到这A的原始对象不需要去创建A了,这样子是不是就打破了这个循环呢?如下图

在这里插入图片描述

看到这里很多人可能就觉得貌似解决这个问题了.但是我们忽略了一个点,那就是Bean的生命周期是很复杂的如果没有其他的操作对于一般的Bean来说确实是可以解决的,但是有些Bean是要经过AOP的,这个时候我们注入的可是AOP产生的代理对象而不是原始对象.那么问题又来了,我们该在什么地方去得到这个代理对象呢?下面我们来详细分析一下A类的生命周期(假设A需要AOP)

1.class

2.Beandifinition

3.实例化

4.填充自动注入属性

5.填充其他属性

这个时候需要去填充A的属性b,那么就需要去创建B了生命周期跟A是一样的

​ 2.1.class

​ 2.2.Beandifinition

​ 2.3.实例化

​ 2.4.填充自动注入属性

​ 填充a–>单例池找–>找不到—>A出现了循环依赖—>生产A的代理对象(AOP)—>二级缓存

​ 2.5.填充其他属性

​ 2.6.AOP

​ 2.7.单例池.

6.AOP

7.单例池.

这里怎么判断A出现了循环依赖呢?其实我们是不是只需要判断A是否还在创建中就能知道A是否出现了循环依赖呢?在这里就又出现了一个重点了creatingSet集合,就是在创建的时候就beanName存入到这个集合中如果集合中有值就说明出现了循环依赖,这个时候我们就需要进行提前AOP,然后将代理对象存入到二级缓存中.

​ 在这里大家可能会有一个疑问,为什么突然出现了一个二级缓存.我们直接把2.4步产生的代理对象赋值给B不就行了么?把a代理对象缓存起来是有什么作用呢?其实这个问题很容易理解的,Spring的单例池是为了保证单例的,加入这个时候有另外一个类C并且依赖了A而且A也依赖的C这个时候是不是还要去注入c属性,那么这个时候如果没有二级缓存是不是A还需要进行一次AOP又产生了一个代理对象,这样子就无法保证单例了.所以看到这里上面的步骤又需要优化一下了

0.creatingSet.add(‘beanName’)

1.class

2.Beandifinition

3.实例化

4.填充自动注入属性–>单例池找(一级缓存singletonObjects)–>找不到–>二级缓存找(earlySingletonObjects)—>找到了—>注入

5.填充其他属性

6.AOP(需要判断是否进行了提前AOP?)

7.单例池.

8.creatingSet.remove(‘beanName’);

一级缓存 singletonObjects CurrentHashMAp<beanName,完整的Bean对象>

二级缓存 earlySingletonObjects HashMap<beanName,不完整的Bean对象>

第三级缓存有什么作用

其实如果没有AOP的话name也不会出现三级缓存了,但是在Bean的生命周期中是可能会出现AOP的,在进行AOP的时候是会用到Bean的原始对象的,在AbstractAutowireCapableBeanFactory这个类中的initializaBean()方法中是是进行初始化和真正的AOP的需要用到Bean的原始对象去生成代理对象的(假如有进行AOP).

那么再回到上面的步骤4中如果二级中找不到在判断当前类出现了循环依赖并且还进行了AOP,这个时候就需要提前AOP了,会用到当前类的原始对象,这个时候我们该怎么拿到当前类的原始对象呢?这个时候大家想起来我们前面提到的那个Map没有呢?在第一步的时候就将原始对象存入一个Map集合中,这个么想算是对了一半,第三级缓存其实是在第一步的是存入了原始对象,但是不单单只是原始对象还有Beandifinition以及BeanName,那么一个Map该如何去存着三个值呢?实现这个方法有很多,Spring是往三级缓存里面存入了BeanName和一个lambda表达式.如下图:

在这里插入图片描述

图上依次是一级缓存,三级缓存和二级缓存.

那么我们最终的步骤就是

0.creatingSet .add(“beanName”)–>记录正在创建中的bean,用来判断是否出现了循环依赖

1.实例化–>new 对象 —>原始对象—>singletonFactories(三级缓存) <beann,lambda(() -> getEarlyBeanReference(beanName, mbd, bean); )

2.填充自动注入属性–>单例池找(一级缓存singletonObjects)–>找不到–>二级缓存找(earlySingletonObjects)–>找不到–>creatingSet—>出现了循环依赖–>三级缓存—>提前AOP—>代理对象–>存入二级缓存–>移除掉三级缓存中对应的值

3.填充其他属性

4.AOP(需要判断是否进行了提前AOP?)

5.单例池.

6.creatingSet.remove(‘beanName’);

现在基本解决了循环依赖,但是还有一个问题就是第四步里面我们如何去判断这个类是否进行了提前AOP.这个时候我们是不是可以借助前面判断是否出现了循环依赖的思路呢?将提前进行AOP的对象存入一个Map集合里面到正常的不走执行AOP的时候判断你Map里面是否存在beanName这个key是不是就能够判断出来是否提前进行过AOP呢?其实我们Spring框架也是这样子做的,这个集合的名字就叫做earlyProxyReferences.

那么现在三级缓存都出现了,在解决循环依赖的时候是出现了五个比较重要的东西的下面来总结一下:

1.creatingSet在spring中是命名为registeredSingletons这个,用来判断是否出现了循环依赖.

2.singletonObjects(一级缓存,单例池)

3.earlySingletonObjects(二级缓存,保存不完整的bean对象)

4.singletonFactories(三级缓存,保存lambda表达式为AOP做准备)

判断是否出现了循环依赖.

2.singletonObjects(一级缓存,单例池)

3.earlySingletonObjects(二级缓存,保存不完整的bean对象)

4.singletonFactories(三级缓存,保存lambda表达式为AOP做准备)

5.earlyProxyReferences用来判断是否提前进行了AOP

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值