java八股之spring的bean生命周期

Bean的生命周期

笼统的流程:实例化-->填充(属性赋值)-->依赖注入-->初始化前-->初始化-->初始化后(AOP)-->放入单例池Map(一级缓存)-->Bean对象

详细一些的流程:A对象-->(无参)构造方法-->普通对象-->依赖注入-->初始化前-->初始化-->初始化后(AOP)-->放入单例池Map(一级缓存)-->Bean对象

解释:依赖注入就是给你A对象里面加了Autowired注解的对象进行依赖注入。

在spring中一个对象通过添加了@Compont注解这时候我们就可以通过它的无参构造方法获得它的bean对象

假设 A中有B对象属性,B中有A对象属性。

如果我们在A对象中通过无参构造方法获得到B对象,这时候需要对B对象进行填充(属性赋值)

这个B对象从哪里拿呢?

答案是 : 单例池(一级缓存)

但是如果我们创建A对象的时候B对象还没有创建(去单例池里面找没找到),这时候就会去创建B对象。那么这时候有人会问为什么我们不赋值个null呢?我们可以点开@Autowired注解。里面默认为true,为true就代表你必须要有值。不能赋值一个null。

这时候我们就会触那么发B对象的bean生命周期 那么就又会重复像A对象步骤一样(构造方法-->有autowired的对象就去单例池找有没有)。。。。。。。。。这样就会无限循环下去。这就是循环依赖。

这时候就要引入三级缓存了。

singletonObjects Map<String,Object>一级缓存(单例池) ConcurrentHashMap

singletonFactories Map<String,ObjectFactory<?>> 三级缓存 HashMap

earlySingletonObjects Map<String,Object> 二级缓存 ConcurrentHashMap

这里我们可以看到其实spring就已经告诉我们 一级 二级 缓存是有线程安全问题的。

三级缓存是工厂模式

循环依赖就是要打破循环,假设我们这时候用自己做的Self缓存来进行一遍流程

A对象创建-->发现B对象需要创建,去一级缓存中没找到,这时候创建B对象的同时把A对象放入Self缓存中,接下来B对象创建-->发现A对象需要创建,去一级缓存没找到,但是去Self缓存中找到了,那么我们是不是就打破了这个循环依赖了呢。???????等等有点不对劲

还记得刚刚的步骤吗,在初始化后有一个AOP操作,那么AOP操作就是动态代理,就会产生一个A代理对象(这里跳转到动态代理)。

其实我们在B中赋值的是A对象但是AOP是赋值为A代理对象的

那么我们要怎么解决呢?

其实这时候我们到打破顺序

在第一步实例化前就做一个creatingSet 放A对象

在一级缓存没有,看看我们A对象在不在creatingSet里面,这样就能判断是否出现循环依赖了

那么我们就在    一级缓存-->creatingSet-->AOP-->Self缓存   中加上AOP。

这样A B循环依赖的问题就解决了,但是!!!!!!!!!!!!!!

我们再换一个场景,这时候加入C对象        C对象引用了A对象(下面伪代码,没写修饰符和注解)

A{
private B b;
}

B{
private A a;
}

C{
private A a;
}

这时候的流程我们再走一遍

1、A对象实例化——》creatingSet A对象——》发现B对象要填充

2、填充B对象 ——》单例池里面找,没找到

3、B对象实例化——》creatingSet B对象——》发现A对象要填充

4、填充A对象——》单例池里面找, 没找到,

5、看看creatingSet里面有没有A对象 有那么进行AOP操作 给B中A填充的A代理对象

6、B对象加入到单例次池中

7、在A对象中 发现C对象要填充

8、填充C对象 ——》单例池里面找,没找到

9、C对象实例化——》creatingSet C对象——》发现A对象要填充

10、填充A对象——》单例池里面找, 没找到,

11、看看creatingSet里面有没有A对象 有那么进行AOP操作   给C中A填充的A代理对象
.....

这里第11点出现了问题,因为我们动态代理导致C填充的A代理对象与B填充的A代理对象不是同一个对象,这就是有问题的。因为是单例,应该是只有一个代理对象赋值给B和C。

那么怎么办呢? 

我们能不能在填充A代理对象的时候给它加入到一级缓存

肯定是不可以的,那么怎么办呢?没有什么事情是加一层解决不了的,如果有那就再加一层,

就需要二级缓存了,

那么就在B填充A对象时步骤就变成了
填充A-->单例池--》creatingSet--》循环依赖-->二级缓存(找有没有A)
--》AOP--》A的代理对象--》二级缓存(放A代理对象)

那么总结一下

一级缓存放的是什么 完整的初始化好的单例bean对象

二级缓存放的是什么 那些因为循环依赖导致要被提前赋值到其他bean对象的bean

那spring为什么需要三级缓存解决循环依赖呢?二级不也行吗?

我们再思考一下还有什么问题吗?

循环依赖 循环依赖 我们需要打破循环,上面点都没有打破循环依赖,并且其实在AOP动态代理中

A代理对象的target是指向A对象的,其实对于AOP我们上面是不成立的。

那么我们来看看三级缓存是如何打破循环依赖的。

其实上面的self缓存就类似于三级缓存了。在A需要填充其他对象时,把A放到三级缓存中。

一旦循环依赖后去二级缓存找,没找到,这时候有一个兜底的三级缓存能找到这个对象,那么循环依赖就会被打破。不一样的点就是

singletonFactories Map<String,ObjectFactory<?>> 三级缓存 HashMap

ObjectFactory是函数式接口(Lambda表达式)附源码 入参(名字,beanDefinition,构造得到的普通对象)

在A做完填充对象 AOP操作后,再加入到单例池前,A是不是还在二级缓存,这时候就把二级缓存的放到一级缓存中。

那么加上循环依赖导致提前AOP了,后面的AOP我们要怎么判断不让他执行呢?

二级缓存!?真的是他吗?

这是正常进入AOP与提前AOP调用的方法 

我们发现上方会进行判断这个earlyProxyReferences是否有

earlyProxyReferences是<Object,Object>  =new ConcurrentHashMap的缓存

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值