spring学习二-spring三级缓存和循环依赖

1 循环依赖介绍

循环依赖是指两个或两个以上bean互相持有对方最终形成闭环。比如A依赖B,B依赖C,C依赖A


循环依赖包括构造器依赖和属性依赖

2 三级缓存解决循环依赖

2.1 spring创建Bean步骤

spring创建bean主要有3个步骤
1 createBeanInstance(实例化bean)
2 populateBean(装配bean)
3 initializeBean(初始化bean)
发生循环依赖的时候主要是在第2步

2.2 三级缓存介绍

spring管理的对象默认是单例的,那么肯定有一个地方来缓存这些对象。spring是通过三级缓存来缓存对象的

/** 一级缓存:bean name和bean实例的缓存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 三级缓存:对象工厂缓存 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 二级缓存:提前早期对象的缓存,还没完成初始化 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

实例化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);
        }
    }
}

这段代码的作用是把实例化的bean从二级缓存移除,放到三级缓存

获取bean的代码

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存中获取对象
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 如果一级缓存没有并且这个对象正在创建则从二级缓存获取
        synchronized (this.singletonObjects) {
            // 从二级缓存获取bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 如果二级缓存也没有则从三级缓存获取
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 如果从三级缓存获取成功则把bean从三级缓存移到二级缓存
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

当bean初始化完全后会放入一级缓存

2.3 三级缓存解决循环依赖

假如有一个场景A引用B,B引用A,三级缓存工作原理如下
1 实例化A对象
2 把还未完全初始化的A暴露到三级缓存singletonFactories
3 装配bean
4 发现依赖B,就get B
5 B还没有被创建,则进行创建
6 B依赖A,所以get A,由于第二步已经把A放到三级缓存,所以顺利地拿到A,完成初始化,把自己放到一级缓存中
7 A顺利地拿到B,完成初始化。
三级缓存只能解决通过属性注入的循环依赖,不能解决通过构造函数注入的循环依赖。因为把bean放入三级缓存的前提是执行构造函数

2.4 三级缓存的必要性

如果只解决循环依赖问题,一级缓存足矣。但是如果仅仅使用一级缓存,那么该缓存里存放的对象既有完全初始化的,又有不完全初始化的。
如果拿到不完全初始化的对象很容易出现NPE
有人说,那好,我再建一个缓存,一级缓存用来存完全初始化的bena,二级缓存用来存不完全初始化的bean,可以了吧。这样是可以的,但是如果
一个对象被做成切面,那么该对象就会生成一个代理对象。这样依赖注入的bean仍是原始的bean,spring会抛异常。
为了解决代理对象注入的问题,加个三级缓存,里面不存具体的bean,里面存一个工厂对象。通过工厂对象,是可以拿到最终形态的代理后的bean
如果想深入了解三级缓存的必要性可以参考这位大佬的文章:spring解决循环依赖为何用三级缓存

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值