Spring循环依赖问题?!三级缓存!!

Java传家宝:微信公众号(Java传家宝)、Java传家宝-B站Java传家宝-知乎Java传家宝-CSND

Spring 三级缓存

​ 说到Spring的三级缓存,我们可以先思考一个问题。即存在两个对象A和B,对象A依赖对象B,而对象B又依赖对象A,那么此时就出现了循环依赖问题,如果要创建A对象,那么必然需要B对象,反之B对象同理,而Spring是如何创建A对象和B对象的呢?这就是循环依赖问题,如下:

@Component
public class A {
    private B b;
}
//==================
@Component
public class B {
    private A a;
}

出现这种情况时,就使用到了Spring的三级缓存。先用大白话来讲:

  • 当Spring执行到实例化对象A完毕,在属性注入时发现b不存在
  • 那么就去创建B对象,此时对象A已经实例化完了,所以属性a注入成功
  • B成功创建完成,返回到A对象的属性注入过程,此时b已经存在
  • A完成b属性注入,初始化完成,得到完整的A对象

注意:前面在为对象B设置属性a的时候,这个A类型的属性a还是个半成品,即a还没有完成b属性注入。但是需要注意的是,这个a是一个引用,所以在最后a的b属性注入完成后,也就相当于B的属性a半成品变成了成品对象。

说的有点绕,说白了就是B之前注入的是半成品的属性a,等到A对象最终完成创建后,B中的a属性才算成品对象。

理论知识

​ 大白话就说到这,下面进行源码分析之前,先理清各级缓存的名字和作用,这里引用西柚dzh的图片:

  • 一级缓存,singletonObjects,concurrentHashMap结构。用于保存完成了实例化、属性注入、初始化以及一系列的后置处理器的调用最终得到的完整的Bean实例对象
  • 二级缓存,earlySingletonObjects,HashMap结构。用于保存完成了实例化,但属性注入未完成的半成品实例对象
  • 三级缓存,singletonFactories,HashMap结构。用于保存Bean的创建工厂,以便后续有机会创建代理对象。

Spring三级缓存

源码分析

我们将A和B作为循环依赖的Bean

​ 前面就不分析了,我们主要看创建Bean A实例这部分的源码,直接进到doGetBean部分,只放了关键部分

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    // 从缓存中取Bean对象
    Object sharedInstance = this.getSingleton(beanName);
    // Create bean instance.
    if (!typeCheckOnly) {
        // 标记对象正在被创建
         this.markBeanAsCreated(beanName);
    }
    if (mbd.isSingleton()) {
     	// 这里就尝试创建目标对象,第二个参数传的就是一个ObjectFactory类型的对象
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                destroySingleton(beanName);
                throw ex;
            }
        });
    }
    return adaptBeanInstance(name, beanInstance, requiredType);
}

可以看到:1、先尝试从缓存中取到对象,如果没取到;2、那么就标记该对象正在被创建;3、然后尝试去创建对象,

首先如何去缓存取得对象:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    	// 去一级缓存取对象
        Object singletonObject = this.singletonObjects.get(beanName);
    	// 没取到 && 该对象正在创建
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            // 去二级缓存中取对象
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 没取到 && 允许早期引用
            if (singletonObject == null && allowEarlyReference) {
                synchronized(this.singletonObjects) {
                    // 重新确定一二级缓存中不存在该Bean
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            // 去三级缓存中取对应的ObjectFactory对象
                            ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                // 三级缓存存在,调用getObject方法返回Bean对象或者Bean的代理对象
                                singletonObject = singletonFactory.getObject();
                                // 添加到二级缓存中
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                // 从一级缓存中移除
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
}

可以看到,步骤如下:

  • 首先去一级缓存singletonObjects中取完整对象,如果没有且当前对象正在被创建中,
  • 那么就去二级缓存earlySingletonObjects中取半成品对象,如果还没取到且allowEarlyReference==true,默认传入的就是true,
  • 那么就第二次确认一二级缓存中确实不存在,然后就去三级缓存中找到对应的ObjectFactory对象
  • 如果存在ObjectFactory对象,那么就调用对应的getObject方法得到Bean对象或者Bean的代理对象,最后返回该对象
  • 上述步骤条件有一个不成立,那么直接返回null即可(一般是该对象不在缓存中也不在创建中)

标记对象就不说了,看一下第三步尝试创建对象,如下:

// AbstractBeanFactory
// 传入了一个函数式方法
sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                destroySingleton(beanName);
                throw ex;
            }
		});
// DefaultSingletonBeanRegistry
public Object getSingleton(String beanName, <?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized(this.singletonObjects) {
            // 先去缓存拿
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //...
                try {
                    // 调用getObject也就是传入的creatBean方法创建Bean或者代理Bean
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } //...
                } finally {
                    //...
                    this.afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    this.addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

我们进入到singletonObject = singletonFactory.getObject()这一步里面,其实就是调用了createBean方法,如下:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    if (instanceWrapper == null) {
        // 创建实例
        instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    }
    //..
    // 保存创建Bean的方法到三级缓存中
        this.addSingletonFactory(beanName, () -> {
            return this.getEarlyBeanReference(beanName, mbd, bean);
        });
    }
    Object exposedObject = bean;
    try {
        // 属性注入
        this.populateBean(beanName, mbd, instanceWrapper);
        // 初始化
        exposedObject = this.initializeBean(beanName, exposedObject, mbd);
    } //...
    }

到这我们就实例化了Bean A并将其对应的创建方法保存到了三级缓存中,它会根据Bean A是否被代理返回对应的原始Bean或者代理Bean。

接下来属性注入时如果遇到循环依赖的Bean B未创建,那么就会通过getBean执行上述一样的流程,将其保存到三级缓存中,然后B在进行属性注入时,在通过getSingleton()取缓存中A实例的时候,会发现三级缓存存在A实例,那么调用getObject获取对象A,并将其添加到二级缓存中。然后为B的属性注入A实例,完成属性注入以及后续的初始化动作。然后将完整的B添加在一级缓存中,移除二三级缓存中的B实例。然后跳转回A的属性注入过程,此时B实例已经缓存了完整对象,直接注入,A的属性注入完成,后续初始化结束,得到完整的A实例,将其添加到一级缓存中,然后删除二三级缓存中的A。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值