Spring源码解析:三级缓存与循环依赖

本文详细介绍了Spring框架如何处理bean之间的循环依赖问题。通过分析`getSingleton`方法的源码,展示了Spring如何利用三级缓存来解决这个问题。在创建bean的过程中,Spring会标记bean为正在创建状态,并在必要时提供早期引用,避免无限递归。文章通过实例解释了循环依赖的产生及Spring的解决策略,帮助读者理解Spring内部的工作机制。
摘要由CSDN通过智能技术生成

循环依赖

Spring的循环依赖指的是两个或以上的bean对象互相作为对方的属性,例如A类中存在属性B,B类中存在属性A。Spring在创建实例化A时发现需要注入B类,就先去创建B类,在创建B类时发现需要注入A类,这时候又去寻找A类。这就是循环依赖,如下图:
在这里插入图片描述
如果按照Spring普通的方式去进行实例化,是不可避免循环依赖的。我们从源码开始,一步一步分析Spring是如何解决循环依赖的。首先是从获取bean开始。我们从doGetBean方法开始进入,注意这个getSingleton(beanName),顾名思义就是根据beanName获取一个单例对象。假如我现在是在获取A类的Bean,那么这里就是通过A的beanName获取bean A。点击进入方法。
在这里插入图片描述
这里包括源码的注释和翻译我一起贴出来

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or {@code null} if none found
	 */
	 /** 
	   * 返回在给定名称下注册的(原始)单例对象。 <p>检查已经实例化的单例,并允许提前引用当前创建的单例(解决循环引用)。
	   */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 从单例缓存中获取单例对象
		Object singletonObject = this.singletonObjects.get(beanName);
		// 如果singletonObject为空,并且对象正在创建中,就进行下一步
		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) {
						// 调用getObject()方法创建一个单例对象
						singletonObject = singletonFactory.getObject();
						// 放入二级缓存
						this.earlySingletonObjects.put(beanName, singletonObject);
						// 从单例工厂中移除
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

首先是从一级singletonObjects中获取bean,这个singletonObjects是一个存放创建完毕的Bean的Map。很明显A正在创建中,肯定获取不到。返回上一步,如果是正在创建的bean的作用域是原型模式,那么就直接抛异常
在这里插入图片描述
但是在实例化之前,Spring会将A类标记为正在创建中的bean,存入一个集合singletonsCurrentlyInCreation中,方便后面处理循环依赖的bean。在DefaultSingletonBeanRegistry类中有一个方法,顾名思义就是在单例创建之前执行的方法。

	/**
	 * Callback before singleton creation.
	 * <p>The default implementation register the singleton as currently in creation.
	 * @param beanName the name of the singleton about to be created
	 * @see #isSingletonCurrentlyInCreation
	 */
	// 单例创建之前的回调。 <p>默认实现将单例注册为当前正在创建。
	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

接下来就是去创建bean A。Spring中一般做事的都是do开头的,所以直接去寻找doCreateBean去实例化A。具体的实例化过程我就不写了,关键在doCreateBean方法中这段代码,如果A类是单例、允许循环依赖、并且是正在创建中的单例。就将bean 存入缓存中,
在这里插入图片描述
这个addSingletonFactory函数式接口的作用就是将bean存入三级缓存。方便其他bean需要注入该bean的时候可以直接获取
在这里插入图片描述
到这里A类就实例化完毕,在为A注入属性的时候九华发现B类需要注入,Spring就会去创建B类的Bean。再次进入我们第一次贴的getSingleton(beanName)方法时,就可以获取到A的bean了。通过我们存入三级缓存的对象工厂直接拿到对象A。B创建完毕后A也能注入了。大致的流程如下图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值