spring的三级缓存:

spring解决循环依赖的核心思想在于提前暴露引用。只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。
一级缓存:单例池Map singletonObjects,存放经过实例化和初始化的对象
二级缓存:earlySingletonObjects二级缓存用于存放已经实例化,但未初始化的Bean.保证一个类多次循环依赖时仅构建一次保证单例,没有经过属性填充,不是完整的对像,保证bean的单例,不会重复创建不完整的对象。
如果某个bean出现了循环依赖,就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,这个bean如果要经过AOP,那么就会把代理对象放入earlySingletonObjects中,否则就是把原始对象放入earlySingletonObjects,但是不管怎么样,就算是代理对象,代理对象所代理的原始对象也是没有经过完整生命周期的,所以放入earlySingletonObjects我们就可以统一认为是未经过完整生命周期的bean。
earlySingletonObjects能够保证未经过完整生命周期的bean的单例,在解决循环依赖的时候,如果这个不完整的对象已经被放入earlySingletonObjects了,在一个类多次循环依赖时,后面就可以直接从earlySingletonObjects中取了, 不用重复创建不完整的对象。
三级缓存:三级缓存用于存放该Bean的ObjectFactory,当创建一个Bean会先将该Bean包装为ObjectFactory放入三级缓存,第三级可以解决AOP问题。
打破循环依赖的是第三级缓存,第三级缓存将代理的bean或者普通的bean提前暴露了出去,可以进行依赖注入。
第三级缓存使解决循环依赖更方便。
总流程图
入口:
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
有循环依赖,才需要提前AOP
改正:在将bean加入三级缓存前,需要判断是否允许循环依赖、是否单例、对象是否在创建中

第0步:AbstractBeanFactory.doGetBean
// 标记beanName AService是已经创建过至少一次的~~~ 它会一直存留在缓存里不会被移除(除非抛出了异常)
protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
if (!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
第一步:
先创建一个普通对象,如果是单例、允许循环依赖、对象正在创建中,则提前暴露引用,将它包装成ObjectFactory放入三级缓存,以下是加入三级缓存的源码

/**
* earlySingletonExposure 是一个重要的变量,这里要说明一下。该变量用于表示是否提前暴露
* 单例 bean,用于解决循环依赖。earlySingletonExposure 由三个条件综合而成,如下:
* 条件1:mbd.isSingleton() - 表示 bean 是否是单例类型
* 条件2:allowCircularReferences - 是否允许循环依赖
* 条件3:isSingletonCurrentlyInCreation(beanName) - 当前 bean 是否处于创建的状态中
*
* earlySingletonExposure = 条件1 && 条件2 && 条件3
* = 单例 && 是否允许循环依赖 && 是否存于创建状态中。
*/
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
/**
* 添加工厂对象到 singletonFactories 缓存中
* 获取早期 bean 的引用,如果 bean 中的方法被 AOP 切点所匹配到,此时 AOP 相关逻辑会介入
* getEarlyBeanReference应用后置处理器SmartInstantiationAwareBeanPostProcessor,允许返回指定bean的早期引用,若没有,则直接返回bean
*/
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
//将bean包装成ObjectFactory,放入三级缓存,同时将bean从二级缓存删除。
//此时的bean没有属性注入,没有初始化,提前进行了引用暴露
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized(this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
第二步
通过beanName从三级缓存中取出一个lamda表达式,
执行getEarlyBeanReference,如果要进行AOP,则返回的是代理对象,如果不要进行AOP,则返回普通对象。然后将返回的对象加入二级缓存

第五步


第六步

Spring三级缓存源码流程图

