在常熟市公司网站建设哪家好,工信部 网站 邮箱,鞍山百姓网,建设部高级职称查询官方网站前言
假设对象A、B 之间相互依赖#xff0c;Spring IOC是如何解决A、B两个对象的实例化的#xff1f;答案是三级缓存。
三级缓存
SpringIOC 通过三级缓存来解决循环依赖问题#xff0c;三级缓存指的是三个Map#xff1a;
singletonObjects#xff1a;一级缓存#xf…前言
假设对象A、B 之间相互依赖Spring IOC是如何解决A、B两个对象的实例化的答案是三级缓存。
三级缓存
SpringIOC 通过三级缓存来解决循环依赖问题三级缓存指的是三个Map
singletonObjects一级缓存key为BeanNamevalue为Bean日常获取Bean的地方earlySingletonObjects二级缓存key为BeanNamevalue为Bean已经实例化但还没有进行属性注入的Bean由三级缓存放入singletonFactories三级缓存key为BeanNamevalue为对象工厂ObjectFactory
在实际使用中要获取一个bean先从一级缓存一直查找到三级缓存缓存bean的时候是从三级到一级的顺序保存并且缓存bean的过程中三个缓存都是互斥的只会保持bean在一个缓存中而且最终都会在一级缓存中。
解决循环依赖
SpringIOC解决循环依赖的思路就是依靠缓存同时还得引出个概念即早期暴露引用。我们知道在IOC容器里Bean的初始化的过程分为三个步骤创建实例、属性注入实例、回调实例实现的接口方法。
解决思路就在这当我们创建实例与属性注入实例这俩个步骤之间的时候我们引入缓存将这些已经创建好但是并没有注入属性的实例放到缓存里而这些放在缓存里但是没有被注入属性的实例对象就是解决循环依赖的方法。
打个比方A对象的创建需要引用到B对象而B对象的创建也需要A对象而此时当B对象创建的时候直接从缓存里引用A对象虽然不是完全体A对象毕竟没有赋值处理当B对象完成创建以后再被A对象引用进去则A对象也完成了创建。
解决循环依赖具体过程
对Bean的创建最为核心三个方法解释如下
createBeanInstance实例化通过反射调用对象构造方法实例化对象populateBean填充属性主要是对bean的依赖属性进行赋值initializeBean初始化可以回调InitializingBean、initMethod等方法。
实例化A的时候先将A创建早期对象放入一个池子singletonFactories中。这个时候虽然属性没有赋值但是容器已经能认识这个是A对象只是属性全是null而已。在populateBean方法中对属性赋值的时候发现A依赖了B那么就先去创建B又走一遍bean的创建过程创建B。同样也会把B的早期对象放入缓存singletonFactories中。当B又走到 populateBean方法负责填充Bean实例属性的方法的时候发现依赖了A我们又去创建A但是这个时候去创建A发现我们在缓存singletonFactories能找到A早期对象此时会通过A的ObjectFactory获取A并把A从三级缓存移到二级缓存。然后就可以把B的A属性赋值了这个时候B就初始化完成了初始化完成后就会把B从三级缓存移到一级缓存。完成B实例化后回到A调用的populateBean方法中。返回的就是B对象了对A的B属性进行赋值就可以了。
流程如下图片来源
IOC无法解决的两种循环依赖
一种是非单例对象因为非单例对象不会放入缓存的。每次都是需要创建。
二是通过构造器注入也无法解决。从上面的流程可以看出调用构造器创建实例是在createBeanInstance方法而解决循环依赖是在populateBean负责属性注入的方法这个方法中执行顺序也决定了无法解决该种循环依赖。
为什么采用三级缓存
一级缓存是单例缓存池singletonObjects
二级缓存是早期对象earlySingletonObjects
三级缓存是一个包裹对象ObjectFactoryregisteredSingletons通过getObject获取到早期对象。
从上面的流程来看实际上二级缓存已经可以解决循环依赖了那么为什么Spring还要包裹出来一个三级缓存呢
三级缓存其实是为了解决代理对象之间AOP的循环依赖。如果没有三级缓存在对象被AOP代理的情况下存入二级缓存前都需要先去做AOP代理。二级缓存存在的必要就是为了性能从三级缓存的工厂创建出对象直接放入二级缓存避免每次都从工厂中获取。.
通过第三级缓存我们可以拿到可能经过包装的对象解决对象代理封装的问题。
三级缓存的value是ObjectFactoryObjectFactory 的 getObject 如果包装的对象被AOP代理则会返回相应的代理对象。
具体源码分析可以参考Spring ioc4—如何解决循环依赖
三级缓存的划分及作用
一级缓存 singletonObjects 是完整的bean它可以被外界任意使用并且不会有歧义。
二级缓存 earlySingletonObjects 是不完整的bean没有完成初始化它与singletonObjects的分离主要是职责的分离以及边界划分可以试想一个Map缓存里既有完整可使用的bean也有不完整的只能持有引用的bean在复杂度很高的架构中很容易出现歧义并带来一些不可预知的错误。
三级缓存 singletonFactories 其职责就是包装一个bean有回调逻辑主要用于解决代理对象的循环依赖所以它的作用非常清晰并且只能处于第三层。
在实际使用中要获取一个bean先从一级缓存一直查找到三级缓存缓存bean的时候是从三级到一级的顺序保存并且缓存bean的过程中三个缓存都是互斥的只会保持bean在一个缓存中而且最终都会在一级缓存中。
总结
SpringIOC 通过三级缓存解决循环依赖要获取一个bean先从一级缓存一直查找到三级缓存缓存bean的时候是从三级到一级的顺序保存并且缓存bean的过程中三个缓存都是互斥的只会保持bean在一个缓存中而且最终都会在一级缓存中。Bean在实例化后createBeanInstance、属性注入前populateBean会先将属性为null的Bean包装成对象工厂ObjectFactory放入三级缓存中在属性注入过程中会依次从一级到三级查询缓存查找依赖的Bean不存在则先实例化依赖的Bean完成属性注入。Bean初始化完成后会被放入一级缓存。 4.三级缓存其实是为了解决代理对象之间AOP的循环依赖通过第三级缓存我们可以拿到可能经过包装的对象解决对象代理封装的问题。
参考
实例创建流程_从Spring Bean创建流程中看三级缓存解决循环依赖好文Spring Bean 循环依赖为什么需要三级缓存