Spring如何解决单例循环依赖

本文详细解析了Spring如何通过三级缓存解决单例Bean的循环依赖问题。介绍了从getSingleton到doCreateBean的调用链,以及在createBeanInstance和populateBean过程中的关键步骤。Spring不支持prototype构造器和单例构造器的循环依赖,并解释了原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。那么spring在自动注入的时候是如何解决这个问题的呢?

答案是:三级缓存,就是三个map,通过提早的暴露对象来解决这个问题。

三级缓存描述

DefaultSingletonBeanRegistry中的源码


	//一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	//三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性
	// 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory,
	// 为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	//二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象
	// 执行了工厂方法生产出来的Bean,bean被放进去之后,
	// 那么当bean在创建过程中,就可以通过getBean方法获取到
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

	//三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

调用链

下面是一个循环依赖的调用链。A->B->A的依赖注入

  • 1.A为正在创建中,反射创建其实例,其对象工厂放入第三层缓存
  • 2.初始化A实例时发现需要依赖注入B,则获取B的实例
  • 3.标记B为正在创建中,反射创建其实例,其对象工厂放入第三层缓存
  • 4.初始化B实例时发现需要依赖注入A,则获取A的实例
  • 5.注意!这时候从缓存中获取时,A为正在创建中且第三层缓存有A的值了,所以调用缓存的对象工厂的getObject方法,把返回的A实例放入第二层缓存,删除第三层缓存
  • 6.B实例初始化完成,放入第一层缓存,移除第二、三层中的缓存
  • 7.回到第2步,A实例初始化完成,放入第一层缓存,移除第二、三层中的缓存

在这里插入图片描述

第一步肯定是要去获取Bean执行getBean(A),getBean->doGetBean

doGetBean中有两个getSingleton方法

getSingleton

从doGetBean方法的源码中依次说明

第一个是从缓存中获取,也就是图片最右边的,由于一开始缓存里面肯定没有所以返回的是null

		// 尝试从单例缓存集合里获取bean实例,也就是图片中的getSingleton(2)
		Object sharedInstance = getSingleton(beanName);

该方法在什么时候会有值呢?如上图最右边依次走到该流程的方法中,这个时候就可以获取到值。首次调用的时候是没有值的,因为还没有放进去。

来看下实现代码

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//尝试从一级缓存里面获取完备的Bean
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果完备的单例还没有创建出来,创建中的Bean的名字会被保存在singletonsCurrentlyInCreation中
		//因此看看是否正在创建
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			//尝试给一级缓存对象加锁,因为接下来就要对缓存对象操作了
			synchronized (this.singletonObjects) {
				//尝试从二级缓存earlySingletonObjects这个存储还没进行属性添加操作的Bean实例缓存中获取
				singletonObject = this.earlySingletonObjects.get(beanName);
				//如果还没有获取到并且第二个参数为true,为true则表示bean允许被循环引用
				if (singletonObject == null && allowEarlyReference) {
					//从三级缓存singletonFactories这个ObjectFactory实例的缓存里尝试获取创建此Bean的单例工厂实例
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					//如果获取到工厂实例
					if (singletonFactory != null) {
						//调用单例工厂的getObject方法返回对象实例
						singletonObject = singletonFactory.getObject();
						//将实例放入二级缓存里
						this.earlySingletonObjects.put(beanName, singletonObject);
						//从三级缓存里移除
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

第二个getSingleton主要是用来创建Bean的

				//如果BeanDefinition为单例
				if (mbd.isSingleton()) {
					//这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							// 显式从单例缓存中删除 bean 实例
							// 因为单例模式下为了解决循环依赖,可能它已经存在了,所以将其销毁
							destroySingleton(beanName);
							throw ex;
						}
					});
					// 如果是普通bean,直接返回,是FactoryBean,返回他的getObject
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

匿名工程ObjectFactory来创建对象createBean->doCreateBean

doCreateBean

该方法才是真正做事情的,重要的几个方法

  • createBeanInstance 通过反射完成对象的实例化,得到半成品对象
  • addSingletonFactory 为了防止循环引用,尽早持有对象的引用,把半成品对象放到三级缓存中
  • populateBean 填充半成品的属性
  • initializeBean 初始化半成品对象
createBeanInstance创建Bean实例

这里创建bean的实例有三种方法

  • 工厂方法创建
  • 构造方法的方式注入
  • 无参构造方法注入
addSingletonFactory
	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);
				//清除此Bean在二级缓存里的缓存信息
				this.earlySingletonObjects.remove(beanName);
				//这里为了记录注册单例的顺序
				this.registeredSingletons.add(beanName);
			}
		}
	}
populateBean填充属性
	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}

		// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		// 给InstantiationAwareBeanPostProcessors最后一次机会在属性注入前修改Bean的属性值,也可以控制是否继续填充Bean
		// 具体通过调用postProcessAfterInstantiation方法,如果调用返回false,表示不必继续进行依赖注入,直接返回
		// 主要是让用户可以自定义属性注入。比如用户实现一个 InstantiationAwareBeanPostProcessor 类型的后置处理器,
		// 并通过 postProcessAfterInstantiation 方法向 bean 的成员变量注入自定义的信息。
		boolean continueWithPropertyPopulation = true;

		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						continueWithPropertyPopulation = false;
						break;
					}
				}
			}
		}
		//如果上面设置 continueWithPropertyPopulation = false,表明用户可能已经自己填充了
		// bean 的属性,不需要 Spring 帮忙填充了。此时直接返回即可
		if (!continueWithPropertyPopulation) {
			return;
		}
		// pvs是一个MutablePropertyValues实例,里面实现了PropertyValues接口,
		// 提供属性的读写操作实现,同时可以通过调用构造函数实现深拷贝
		//获取BeanDefinition里面为Bean设置上的属性值
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
		// 根据Bean配置的依赖注入方式完成注入,默认是0,即不走以下逻辑,所有的依赖注入都需要在xml文件中有显式的配置
		// 如果设置了相关的依赖装配方式,会遍历Bean中的属性,根据类型或名称来完成相应注入,无需额外配置
		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			// 根据beanName进行autowiring自动装配处理
			// 	<bean id="boyFriend" class="com.bushro.dao.impl.BoyFriend"  autowire="byName"></bean>
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			//根据Bean的类型进行autowiring自动装配处理
			//	<bean id="boyFriend" class="com.bushro.dao.impl.BoyFriend"  autowire="byType"></bean>
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}
		// 容器是否注册了InstantiationAwareBeanPostProcessor
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		// 是否进行依赖检查,默认为false
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

		PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					//在这里会对@Autowired标记的属性进行依赖注入
					PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						if (filteredPds == null) {
							filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
						}
						// 对解析完但未设置的属性再进行处理
						pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvsToUse == null) {
							return;
						}
					}
					pvs = pvsToUse;
				}
			}
		}
		// 依赖检查,对应depend-on属性,3.0已经弃用此属性
		if (needsDepCheck) {
			// 过滤出所有需要进行依赖检查的属性编辑器
			if (filteredPds == null) {
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}

		if (pvs != null) {
			//最终将属性注入到Bean的Wrapper实例里,这里的注入主要是供
			//显式配置了autowiredbyName或者ByType的属性注入,
			//针对注解来讲,由于在AutowiredAnnotationBeanPostProcessor已经完成了注入,
			//所以此处不执行
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

在遍历BeanPostProcessor的时候有一个AutowiredAnnotationBeanPostProcessor类,该类的postProcessProperties方法来进行注入

	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		//获取指定类中@Autowired相关注解的元信息
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
			//对Bean的属性进行自动注入
			metadata.inject(bean, beanName, pvs);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}

在创建A的时候如果发现属性B没有实例化的话,就又会创建A的方法…

循环依赖的支持情况

Spring只能实现单例bean通过set注入或者@Autowired进行循环依赖

  • 构造器循环依赖【singleton(不支持)、prototype(不支持)】
  • Setter注入循环依赖【singleton、prototype(不支持)】

三级缓存除了解决单例循环依赖的问题,还解决了Bean是唯一的问题。而prototype类型的Bean不是唯一的,所以是不能使用这种方式来解决的

不支持prototype构造器循环依赖

例如:

@Component
@Scope(value = "prototype")
public class A {
	private B b;

	@Autowired
	public A(B b) {
		this.b = b;
	}
}
@Component
@Scope(value = "prototype")
public class B {
	private A a;

	@Autowired
	public B(A a) {
		this.a = a;
	}
}

在AbstractBeanFactory->doGetBean方法中

prototypesCurrentlyInCreation是一个ThreadLocal类型的变量。产生循环依赖的时候该变量就会有值,从而抛出异常

	//如果scope为prototype并且显示还在创建中,则基本是循环依赖的情况
	//针对prototype的循环依赖,spring无解,直接抛出异常
	if (isPrototypeCurrentlyInCreation(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}

	/**
	 * 返回指定的原型bean当前是否正在创建中
	 */
	protected boolean isPrototypeCurrentlyInCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		return (curVal != null &&
				(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
	}

prototype类型的bean在spirng启动的时候并不会去初始化,只有在使用的时候才会去初始化。执行getBean的时候才会去加载。

AbstractApplicationContext->refresh->finishBeanFactoryInitialization->preInstantiateSingletons

初始话的是单例bean,并且不是懒加载的

在这里插入图片描述

不支持单例构造器循环依赖

例如

@Component
public class A {
	private B b;

	@Autowired
	public A(B b) {
		this.b = b;
	}
}
@Component
public class B {
	private A a;

	@Autowired
	public B(A a) {
		this.a = a;
	}
}

不支持的原因是构造器的实例化是在doCreateBean中的createBeanInstance,此时还没有缓存,在实例化A的时候发现B还没有进行实例化就去执行B的构造器来实例话,这样就形成死循环了。

		// 使用带参的构造函数进行装配
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

Setter注入循环依赖是在populateBean 填充半成品的属性的时候来注入,这个时候是已经有缓存了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

原飞木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值