博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
彻底讲懂Spring singletonBean循环依赖与三级缓存
阅读量:2141 次
发布时间:2019-04-30

本文共 3861 字,大约阅读时间需要 12 分钟。

这里指的是单例的、非构造依赖的循环引用。很多人都知道Spring用了三层缓存来解决循环依赖,但是不知道其原因,为什么是三级缓存?二级缓存不行吗?一级缓存不可以 ?如果是构造函数中存在循环依赖,则报错。

bean的流程:

beanDefinition->bean实例化->填充属性->aware->beanPostprocessor.before->init->beanPostProcessor.after->单例池+

三级缓存

Spring 解决循环依赖的核心就是提前暴露对象,而提前暴露的对象就是放置于第二级缓存中。缓存的底层都是Map,至于它们属于第几层是由Spring获取数据顺序以及其作用来表现的。

三级缓存的说明:

名称 作用
singletonObjects 一级缓存,存放初始化完成的Bean。
earlySingletonObjects 二级缓存,存放提前暴露的Bean,Bean 是不完整的,未完成属性注入和执行 初始化(init) 方法。
singletonFactories 三级缓存,存放的是 Bean 工厂,主要是生产 Bean,存放到二级缓存中。

第一 先说说第一级缓存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接口签名的回调实现
3.这里还涉及到动态代理的提前,用earlyProxyReferences这个map记录被提前动态代理的bean。

# 函数签名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 = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }复制代码

这里的getEarlyBeanReference方法就是得到代理对象。

AbstractAutoProxyCreatorl类中getEarlyBeanReference的方法

@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey);}

会有一个earlyProxyReference缓存来保存代理对象,如果循环代理中有AOP则将原始bean对象存入earlyProxyReference,用于之后的比较,判断这个bean的AOP是不是提前做过,然后通过**wrapIfNecessary(bean,beanName,cacheKay)**生成代理对象。

@Overridepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey); } } return bean;}

在bean初始化之后,会回调AbstractAutoProxyCreatorl类中的postProcessAfterInitialization方法,如果从earlyProxyReference中获取的对象是原始bean,则说明动态代理还没进行过,表示这是在正常逻辑中的AOP,没有循环依赖的过程,则表示AOP没有提前过,通过**wrapIfNecessary(bean,beanName,cacheKay)**生成代理对象,反之,标明AOP过程已经提前。

3.通过第三级缓存我们可以拿到可能经过包装的对象,解决对象代理封装的问题。

第三 说说第二级缓存earlySingletonObjects

1.为什么需要earlySingletonObjects这个二级缓存?并且,如果只有一个缓存的情况下,为什么不直接使用singletonFactories这个缓存,即可实现代理又可以缓存数据。

2.从软件设计角度考虑,三个缓存代表三种不同的职责,根据单一职责原理,从设计角度就需分离三种职责的缓存,所以形成三级缓存的状态。
3、再次说说三级缓存的划分及其作用。
一级缓存singletonObjects是完整的bean,它可以被外界任意使用,并且不会有歧义。
二级缓存earlySingletonObjects是不完整的bean,没有完成初始化,它与singletonObjects的分离主要是职责的分离以及边界划分,可以试想一个Map缓存里既有完整可使用的bean,也有不完整的,只能持有引用的bean,在复杂度很高的架构中,很容易出现歧义,并带来一些不可预知的错误。
三级缓存singletonFactories,其职责就是包装一个bean,有回调逻辑,所以它的作用非常清晰,并且只能处于第三层。
在实际使用中,要获取一个bean,先从一级缓存一直查找到三级缓存,缓存bean的时候是从三级到一级的顺序保存,并且缓存bean的过程中,三个缓存都是互斥的,只会保持bean在一个缓存中,而且,最终都会在一级缓存中。

语雀地址

转载地址:http://okagf.baihongyu.com/

你可能感兴趣的文章
【TED】只需专注10分钟-Andy Puddicombe
查看>>
【LEETCODE】292-Nim Game
查看>>
【LEETCODE】237-Delete Node in a Linked List
查看>>
【LEETCODE】206-Reverse Linked List
查看>>
【LEETCODE】203-Remove Linked List Elements
查看>>
【LEETCODE】234-Palindrome Linked List
查看>>
【LEETCODE】141-Linked List Cycle
查看>>
【LEETCODE】142-Linked List Cycle II
查看>>
【LEETCODE】92-Reverse Linked List II
查看>>
【LEETCODE】283-Move Zeroes
查看>>
【LEETCODE】217-Contains Duplicate
查看>>
【LEETCODE】219-Contains Duplicate II
查看>>
【LEETCODE】220-Contains Duplicate III
查看>>
【LEETCODE】171-Excel Sheet Column Number
查看>>
【LEETCODE】169-Majority Element
查看>>
【LEETCODE】191-Number of 1 Bits
查看>>
【LEETCODE】13-Roman to Integer
查看>>
【LEETCODE】83-Remove Duplicates from Sorted List
查看>>
【LEETCODE】70-Climbing Stairs
查看>>
【LEETCODE】198-House Robber
查看>>