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的缓存