spring的三级缓存 解决循环依赖

三级缓存的方式解决
三级缓存用于存储单例的Bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储
从三个缓存中依次获取指定的Bean实例。 读取顺序依次是一级缓存–>二级缓存–>三级缓存
第一级缓存:用于存储单例模式下创建的Bean实例(已经创建完毕)
第二级缓存:早期曝光对象(如果被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象)
第三季缓存:早期曝光对象工厂(singletonFactories)

对于Spring循环依赖的情况总结如下:

  • 不能解决的情况:1. 构造器注入循环依赖 2. prototype field属性注入循环依赖
  • 能解决的情况:1. field属性注入(setter方法注入)循环依赖

「Spring的循环依赖的理论依据基于Java的引用传递」,当获得对象的引用时,「对象的属性是可以延后设置的」。

spring创建bean的流程:在这里插入图片描述

对Bean的创建最为核心三个方法解释如下:

  • createBeanInstance:例化,其实也就是调用对象的「构造方法」实例化对象
  • populateBean:填充属性,这一步主要是对bean的依赖属性进行注入(@Autowired)
  • initializeBean:回到一些形如initMethod、InitializingBean等方法

从对单例Bean的初始化可以看出,循环依赖主要发生在「第二步(populateBean)」,也就是field属性注入的处理。

三级缓存

这三级缓存分别指:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
 ...
 // 从上至下 分表代表这“三级缓存”
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
 ...
 
 /** Names of beans that are currently in creation. */
 // 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
 // 它在Bean开始创建时放值,创建完成时会将其移出~
 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

 /** Names of beans that have already been created at least once. */
 // 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
 // 至少被创建了一次的  都会放进这里~~~~
 private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}

注:AbstractBeanFactory继承自DefaultSingletonBeanRegistry~

  • singletonObjects:用于存放完全初始化好的 bean,「从该缓存中取出的 bean 可以直接使用」
  • earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  • singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

获取单例Bean:
先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。「(如果获取到了就从」singletonFactories「中移除,并且放进」earlySingletonObjects「。其实也就是从三级缓存」移动(是剪切、不是复制哦~)「到了二级缓存)」

「加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决」

getSingleton()从缓存里获取单例对象步骤分析可知,Spring解决循环依赖的诀窍:「就在于singletonFactories这个三级缓存」。这个Cache里面都是ObjectFactory,它是解决问题的关键。

经过ObjectFactory.getObject()后,此时放进了二级缓存earlySingletonObjects内。这个时候对象已经实例化了,虽然还不完美,但是对象的引用已经可以被其它引用了

Spring的Bean创建过程
首先创建早期对象(还没有实例化的对象),然后添加到早期对象
首先创建Bean实例的包装类,方便后面使用

 * 真正的创建bean实例(这里创建实例包装类),调用构造方法或者工厂方法
 */
instanceWrapper = createBeanInstance(beanName, mbd, args);

添加到我们的缓存中,这里就是我们的单利缓存池,解决循环依赖的关键所在

/**
 * 添加到我们的单例工厂中缓存起来
 *
 * 提早暴露早期对象 (还没有进行初始化的对象)  就是还没有进行赋值的对象就是早期对象
 *
 */
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

然后就是Bean的赋值,初始化Bean

/**
 * 给我们的Bean进行赋值
 */
populateBean(beanName, mbd, instanceWrapper);
/**
 * 初始化bean
 */
exposedObject = initializeBean(beanName, exposedObject, mbd);

一级缓存,singletonObjects存的是完成创建的对象,也就是实例化了的对象。
二级缓存是earlySingletonObjects,存储早期对象
三级缓存是singletonFactory,在creatBeanInstance时候放入

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

    /**
     * singletonObjects 这个就是我们大名鼎鼎的单例缓存池
     */
    Object singletonObject = this.singletonObjects.get(beanName);

    /**
     * singletonObject == null 并且单例正在创建
     *
     * isSingletonCurrentlyInCreation就是个标识,标识这个bean是否正在被创建。
     */
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
           //去早期对象里面去获取,如果获取到了就直接返回了。
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
               //如果早期对象没获取到,就去单例工厂里面获取
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                   //如果单例工厂取到了 ,加到早期对象中,然后从单例工厂中移除
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

如果没捞到,那继续去父工厂中捞一波,如果拿到就返回。

/**
 * 如果缓存中没有拿到,就去检查父工厂里面有没有
 */
BeanFactory parentBeanFactory = getParentBeanFactory();

如果还是没有,那只能自己创建对象了

 /**
  * 真正的创建bean实例了,调用构造方法或者工厂方法
  */
instanceWrapper = createBeanInstance(beanName, mbd, args);

把早期对象添加到我们的单例缓存池中

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");

    /**
     * singletonObjects 单例缓存池
     * singletonFactories  单例工厂
     * earlySingletonObjects 早期单例对象
     */
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

二级缓存作用

A放入三级缓存中是一个factory对象,是因为spring目前还无法判断,A对象在后续是否存在循环依赖,所以factory对象的目的在于,如果发生了循环依赖,则需要创建被增强的对象,而为什么又是被增强的对象呢,因为,我记得spring对bean的增强统一在后置处理中,但是由于被依赖,所以必须提前创建好被增强的对象,放入到二级缓存中。否则在程序运行中是代理对象A,而B拿到的对象却是普通对象A。
但是那跟放入二级缓存有啥关系呀,那是因为,如果不放入二级缓存中,那只能还是从三级缓存中获取,那么会再次触发aop增强
多一级缓存意义就是在bean循环引用过程中,发生了AOP增强,避免返回真实对象而非代理对象Proxy

spring为什么不支持构造器循环依赖

因为A中构造器注入了B,那么A在关键的方法addSingletonFactory()之前就去初始化了B,导致三级缓存中根本没有A,所以会发生死循环,Spring发现之后就抛出异常了。至于Spring是如何发现异常的呢,本质上是根据Bean的状态给Bean进行mark,如果递归调用时发现bean当时正在创建中,那么久抛出循环依赖的异常即可

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值