循环依赖
普通对象的循环依赖
1、先实例化A对象,未初始化,半成品 A@1536
2、将实例化A对象的lambda表达式放入三级缓存
3、A对象填充b属性值,b是一个RuntimeBeanReference
4、从容器中的缓存取B对象,取不到,创建实例化B对象,半成品 B@1937
5、将实例化B对象的lambda表达式放入三级缓存
6、B对象填充a属性值,a也是一个RuntimeBeanReference,尝试从容器的1,2,3缓存中依次取key为a的beanName,从三级缓存中取到key为a,value是lambda表达式
7、调用singletonFactory.getObject(),函数式接口的唯一方法,进去lambda表达式的方法,getEarlyBeanReference(beanName, mbd, bean)获取到半成品的A对象 A@1536
8、将半成品的 A@1536 放入二级缓存,将三级缓存中k:a—v:lambda表达式 移除,然后将半成品A对象 填充到B对象中的a属性
9、此时B对象是成品对象,放入一级缓存中,把二,三级缓存中bean为b的移除
9、填充A对象中b属性值,此时A对象也是成品对象,放入一级缓存中,把二,三级缓存中bean为a的移除
循环依赖的对象,不需要代理的话,只需要二级缓存可以解决所有问题,但是当存在代理之后就无法解决了,必须要使用三级缓存来解决
AOP 代理对象的生成是在成品对象创建完成之后,在获取具体的对象的时候,直接通过lambda表达式动态生成对应的代理对象,也就是通过BPP的after()方法创建的,
普通对象会被代理对象覆盖,最终放入一级缓存
AOP代理对象的循环依赖
1、先实例化A对象,未初始化,半成品 A@1536
2、将实例化A对象的lambda表达式放入三级缓存
3、A对象填充b属性值,b是一个RuntimeBeanReference
4、从容器中的缓存取B对象,取不到,创建实例化B对象,半成品 B@1937
5、将实例化B对象的lambda表达式放入三级缓存
6、B对象填充a属性值,a也是一个RuntimeBeanReference,尝试从容器的1,2,3缓存中依次取key为a的beanName,value是lambda表达式
7、调用singletonFactory.getObject(),接口的唯一方法,进去lambda表达式的方法,getEarlyBeanReference(beanName, mbd, bean)获取到半成品的A对象 A@1536,经过BeanPostProcessor的after生成代理A对象
8、将A代理对象放入二级缓存,将三级缓存中k:a—v:lambda表达式 移除,然后将代理A对象 填充到B对象中的a属性,B是成品对象,然后经过BeanPostProcessor的after生成代理B对象
9、此时代理B对象是成品对象,放入一级缓存中,把二,三级缓存中bean为b的移除
10、填充A对象中b属性值,此时A对象也是成品对象,放入一级缓存中,把二,三级缓存中bean为a的移除
第一级缓存:对外暴露的对象,属性已填充的完整对象
第二级缓存:为了处理循环依赖的对象创建问题,存的是半成品对象或半成品对象的代理对象
第三级缓存:处理存在 AOP + 循环依赖的对象创建问题,能将代理对象提前创建,提前暴露的对象,存放已经创建完成但还没有注入好的对象的工厂对象,通过这个工厂可以返回代理对象
Spring 是如何解决循环依赖的问题的
三级缓存,提前暴露对象,aop
总:循环依赖问题,A依赖B,B依赖A
分:先说明bean的创建过程:实例化,初始化(填充属性)
1、先创建A对象,实例化A对象,此时A对象中的b属性为空,填充属性b
2、从容器中查找B对象,如果找到了,直接赋值不存在循环依赖问题(不通),找不到直接创建B对象
3、实例化B对象,此时B对象中的a属性为空,填充属性a
4、从容器中查找A对象,找不到,直接创建
形成闭环的原因:
A对象是存在的,此时的A对象不是一个完整的状态,只完成了实例化但未完成初始化,可以优先把非完整状态的对象优先赋值,等待后续操作来完成赋值,相当于提前暴露了某个不完整对象的引用,解决问题的核心在于实例化和初始化分开操作,是解决循环依赖问题的关键。
当所有的对象都完成实例化和初始化操作之后,还要把完整对象放到容器中,此时在容器中存在对象的几个状态,完成实例化但未完成初始化状态与完整状态,所以需要不同的map结构来进行存储,
就有了一级缓存和二级缓存,如果一级缓存中有了,那么二级缓存中就不会存在同名的对象,因为查找顺序是1,2,3这样的方式来查找的。一级缓存中放的是完整对象,二级缓存中放的是非完整对象。
为什么需要三级缓存?三级缓存的value类型是ObjectFactory,是一个函数式接口,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个。
如果一个对象需要被代理或需要生成代理对象,要优先生成一个普通对象。
普通对象和代理对象是不能同时出现在容器中的,因此当一个对象需要被代理的时候,就要用代理对象覆盖掉之前的普通对象,在实际的调用过程中,是没有办法确定什么时候对象被使用,所以当某个对象被调用的时候,优先判断此对象是否需要被代理,类似于一种回调机制的实现,可以通过lambda表达式来执行对象的覆盖过程,getEarlyBeanReference()。
因此,所有的bean对象在创建的时候都要优先放到三级缓存中,在后续的使用过程中,如果需要被代理则返回代理对象,如果不需要被代理,则直接返回普通对象
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)