深入源码分析Spring为什么不支持构造方法的循环依赖

我们知道当通过构造方法的方式注入属性时,是不支持循环依赖这种场景的,本文主要通过分析源码看看为什么构造方法不能支持循环依赖。

当然,如果读者还不了解循环依赖的问题,建议先结合源码搞清楚,可以先看看这篇文章,
深入源码分析Spring如何解决循环依赖,否则很有可能看不懂本文内容。

先看问题

@Component
public class TestA {

    private final TestB testB;

    public TestA(TestB testB) {
        this.testB = testB;
    }

}
@Component
public class TestB {

    private final TestA testA;

    public TestB(TestA testA) {
        this.testA = testA;
    }
}

TestA和TestB通过构造方法互相注入依赖,结果启动报错咯!
在这里插入图片描述
进入源码分析

假设先准备实例化TestA对象,直接找到实例化关键代码部分。

				//省略部分源码。。。
				if (mbd.isSingleton()) {
					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.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				//省略部分源码。。。
	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				//这个方法发挥了重要的作用
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					//调用createBean方法
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

beforeSingletonCreation,上编文章分析过,把当前bean放到一个set集合中,错误就是从这抛出来的,也就是放入set集合失败了,稍微我们再来分析,第一次肯定是没问题的。

	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

createBean方法

	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		//省略部分源码。。。
		try {
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		//省略部分源码。。。
	}

doCreateBean 这个是核心方法,本文就关注实例化这个方法,在非构造方法依赖的情况下,这个方法是通过反射调用无参构造方法实例化的,而现在不行了。

当Spring发现有autowiring的构造函数,就会通过这个构造函数进行实例化。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
			//省略部分源码。。。
			if (instanceWrapper == null) {
			//实例化
			instanceWrapper = createBeanInstance(beanName, mbd, args);
			//省略部分源码。。。
		}
}
	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		
		//省略部分源码。。。
		
		// Candidate constructors for autowiring?
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			//有参调用这个方法
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// Preferred constructors for default construction?
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// No special handling: simply use no-arg constructor.
		//无参构造函数是调用这个方法
		return instantiateBean(beanName, mbd);
	}

autowireConstructor

	public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
			//省略部分源码。。。
		argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
								getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
			//省略部分源码。。。
								
	}

createArgumentArray

	private ArgumentsHolder createArgumentArray(
			String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
			BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
			boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
		//省略部分源码。。。
		try {
					Object autowiredArgument = resolveAutowiredArgument(
							methodParam, beanName, autowiredBeanNames, converter, fallback);
					args.rawArguments[paramIndex] = autowiredArgument;
					args.arguments[paramIndex] = autowiredArgument;
					args.preparedArguments[paramIndex] = autowiredArgumentMarker;
					args.resolveNecessary = true;
				}
				catch (BeansException ex) {
					throw new UnsatisfiedDependencyException(
							mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
				}
				//省略部分源码。。。
	}

resolveAutowiredArgument

	@Nullable
	protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
			@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
			//省略部分源码。。。
		try {
			return this.beanFactory.resolveDependency(
					new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
		}
		catch (NoUniqueBeanDefinitionException ex) {
			throw ex;
		}
			//省略部分源码。。。
	}

resolveDependency这个方法在 深入源码分析Spring如何解决循环依赖 文中有看过,最终会调用到beanName的getBean方法。

源码看到这边已经足够了,我们可以看出与无参构造函数不同之处在于,依赖属性的getBean调用提前了,无参构造函数是在类实例化完成,并且放入到了三级缓存中之后才调用的,而现在不是了,也就是说现在缓存中并没有TestA。

所以当调用getBean(testB)时,同样会走到getBean(testA),此时缓存中没有testA,那么就又会反复执行testA的实例化过程,那么是在什么地方终止的呢?

就是这个方法了,此时testA要想再加入这个set集合就会返回false了,因为第一次testA的实例化过程就已经放入这个set集合了,所以此处就抛出了异常,终止了整个流程。

	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

构造方式依赖注入
在这里插入图片描述
@Autowired属性依赖注入
在这里插入图片描述
总结

通过构造方法之所以失败的主要原因就是,依赖的属性被提前实例化了,因为testA未完成实例化,内存中也就不存在有关testA的任何记录,自然缓存中也没有,所以当再次触发testA的实例化时,也没法从缓存中获取,最终通过一个set集合判断是否被重复添加来控制,避免陷入死循环。。。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Spring循环依赖是指两个或多个Bean之间相互依赖,形成一个循环引用的关系。在Spring容器启动的过程中,如果存在循环依赖,那么默认情况下会抛出BeanCurrentlyInCreationException异常。 下面是Spring循环依赖源码分析: 1. 当我们向Spring容器中注入一个Bean时,Spring会先检查这个Bean是否已经正在创建中(正在创建的Bean是无法注入的),如果正在创建中,则直接返回一个早期引用,否则继续创建Bean。 2. 在Bean的创建过程中,当遇到依赖注入(如@Autowired注解)时,Spring会检查要注入的Bean是否已经在创建中。如果是,则返回一个代理对象作为占位符,等待真正的Bean创建完毕后再进行注入。 3. Spring使用三级缓存来解决循环依赖问题。第一级缓存是单例池,存放已经创建好的单例Bean。第二级缓存是提前暴露的ObjectFactory,存放早期引用。第三级缓存是用于存放正在创建中的Bean的缓存,用于检测循环依赖。 4. 当检测到循环依赖时,Spring会尝试使用构造函数的方式完成循环依赖。它会先创建一个空对象,并将其放入到第三级缓存中。然后调用构造函数去创建这个Bean,此时依赖的Bean会返回一个早期引用。最后,将这个Bean加入到第一级缓存中,并开始注入依赖。 5. 当所有的Bean都创建完成后,Spring会触发后置处理器的回调方法,完成Bean的初始化。 总结:Spring循环依赖通过使用三级缓存和构造函数来解决,在Bean创建过程中动态地判断和处理循环依赖关系,确保所有的Bean都能被正确地创建和注入
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码拉松

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

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

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

打赏作者

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

抵扣说明:

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

余额充值