SpringBean循环依赖为什么需要三级缓存实例化bean的三种方式
Spring Bean 循环依赖为什么需要三级缓存
这⾥指的是单例的、⾮构造依赖的循环引⽤。很多⼈都知道Spring⽤了三层缓存来解决循环依赖,但是不知道其原因,为什么是三级缓存?⼆级缓存不⾏吗?⼀级缓存不可以?
三级缓存
Spring 解决循环依赖的核⼼就是提前暴露对象,⽽提前暴露的对象就是放置于第⼆级缓存中。缓存的底层都是Map,⾄于它们属于第⼏层是由Spring获取数据顺序以及其作⽤来表现的。
三级缓存的说明:
名称作⽤
singletonObjects⼀级缓存,存放完整的 Bean。
earlySingletonObjects⼆级缓存,存放提前暴露的Bean,Bean 是不完整的,未完成属性注⼊和执⾏初始化(init)⽅法。singletonFactories三级缓存,存放的是 Bean ⼯⼚,主要是⽣产 Bean,存放到⼆级缓存中。
在DefaultSingletonBeanRegistry类中:
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
为什么使⽤三级缓存?
第⼀先说说第⼀级缓存singletonObjects
1.先说⼀级缓存singletonObjects。实际上,⼀级依赖已经可以解决循环依赖的问题,假设两个beanA和beanB相互依赖,beanA被实例化后,放⼊⼀级缓存,即使没有进⾏初始化,但是beanA的引⽤已经创建(栈到堆的引⽤已经确定),其他依赖beanB已经可以持有beanA的引⽤,但是这个bean在没有初始
化完成前,其内存(堆)⾥的字段、⽅法等还不能正常使⽤,but,这并不影响对象之间引⽤持有;这个时候beanA依赖的beanB实例化,beanB可以顺利拿到beanA的引⽤,完成beanB的实例化与初始化,并放⼊⼀级缓存,在beanB完成创建
后,beanA通过缓存顺利拿到beanB的引⽤,⾄此,循环依赖只需⼀层缓存就能完成。
2.⼀级缓存的关键点在与:bean实例化与初始化的分离。从JVM的⾓度,实例化后,对象已经存在,其内的属性都是初始默认值,只有在初始化后才会赋值,以及持有其他对象的引⽤。通过这个特性,在实例化后,我们就可以将对象的引⽤放⼊缓存交给需要引⽤依赖的其他对象,这个过程就是提前暴露。
第⼆说说第三级缓存singletonFactories
1.上述我们通过⼀级缓存已经拿到的对象有什么问题?
根本问题就是,我们拿到的是bean的原始引⽤,如果我们需要的是bean的代理对象怎么办?Spring⾥充斥了⼤量的动态代理模式的架构,典型的AOP就是动态代理模式实现的,再⽐如我们经常使⽤的配置类注解@Configuration在缺省情况下(full mode),其内的所有@Bean都是处于动态代理模式,除⾮⼿动指定proxyBeanMethods = false将配置转成简略模式(lite mode)。
2.所以,Spring在bean实例化后,将原始bean放⼊第三级缓存singletonFactories中,第三级缓存⾥实际
存⼊的是ObjectFactory接⼝签名的回调实现。
# 函数签名
addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
# 具体实现由回调决定
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
那么如果有动态代理的需求,⾥⾯可以埋点进⾏处理,将原始bean包装后返回。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = EarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
3.通过第三级缓存我们可以拿到可能经过包装的对象,解决对象代理封装的问题。
第三说说第⼆级缓存earlySingletonObjects
1.为什么需要earlySingletonObjects这个⼆级缓存?并且,如果只有⼀个缓存的情况下,为什么不直接使⽤singletonFactories这个缓存,即可实现代理⼜可以缓存数据。
2.从软件设计⾓度考虑,三个缓存代表三种不同的职责,根据单⼀职责原理,从设计⾓度就需分离三种职责的缓存,所以形成三级缓存的状态。
3、再次说说三级缓存的划分及其作⽤。
⼀级缓存singletonObjects是完整的bean,它可以被外界任意使⽤,并且不会有歧义。
⼆级缓存earlySingletonObjects是不完整的bean,没有完成初始化,它与singletonObjects的分离主要是职责的分离以及边界划分,可以试想⼀个Map缓存⾥既有完整可使⽤的bean,也有不完整的,只能持有引⽤的bean,在复杂度很⾼的架构中,很容易出现歧义,并带来⼀些不可预知的错误。
三级缓存singletonFactories,其职责就是包装⼀个bean,有回调逻辑,所以它的作⽤⾮常清晰,并且只能处于第三层。
在实际使⽤中,要获取⼀个bean,先从⼀级缓存⼀直查到三级缓存,缓存bean的时候是从三级到⼀级的顺序保存,并且缓存bean的过程中,三个缓存都是互斥的,只会保持bean在⼀个缓存中,⽽且,最终都会在⼀级缓存中。
总结
还不到总结的时候... ...
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论