【源码解析】SpringBoot循环依赖源码解析II

news/2024/11/28 17:23:58/

前言

前面已经写过循环以来的分析,对循环依赖有一些了解,【源码解析】Spring循环依赖和三级缓存。简单回顾一下:

  • Spring可以解决@Autowired注入的循环依赖

  • Spring解决不了构造器注入的循环依赖

  • 使用@Aysnc注解会导致循环依赖。提前暴露的Bean是基础的切面类拦截生成的代理,而Bean生成后会调用后置处理器(包含@Async),Spring判断两者不一致,会报错

问题发现

SpringBoot版本升级到2.6.13后,准确来说升级到2.6之后,使用如下代码也会报错。

@Service
public class ServiceA {@Autowiredprivate ServiceB serviceB;
}
@Service
public class ServiceB {@Autowiredprivate ServiceA serviceA;
}

报错信息如下:

Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐
|  serviceA (field private com.example.demospring2.cmp.ServiceB com.example.demospring2.cmp.ServiceA.serviceB)
↑     ↓
|  serviceB (field private com.example.demospring2.cmp.ServiceA com.example.demospring2.cmp.ServiceB.serviceA)
└─────┘Action:Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

需要添加配置spring.main.allow-circular-references=true

源码分析

ServiceA需要ServiceB实例,创建ServiceBServiceB创建又需要ServiceA,核心就在第二次获取ServiceA的时候,报错。

AbstractBeanFactory#doGetBean,再次获取ServiceA,调用DefaultSingletonBeanRegistry#getSingleton(String)方法获取不到,进行了第二次的创建。

	protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object beanInstance;// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate").tag("beanName", name);try {if (requiredType != null) {beanCreation.tag("beanType", requiredType::toString);}RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);try {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// Create bean instance.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;}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {String scopeName = mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");}Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new ScopeNotActiveException(beanName, scopeName, ex);}}}catch (BeansException ex) {beanCreation.tag("exception", ex.getClass().toString());beanCreation.tag("message", String.valueOf(ex.getMessage()));cleanupAfterBeanCreationFailure(beanName);throw ex;}finally {beanCreation.end();}}return adaptBeanInstance(name, beanInstance, requiredType);}

DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>),创建实例,会进行判断

	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 {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;}}

DefaultSingletonBeanRegistry#beforeSingletonCreation,这是循环依赖的报错。

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

这个流程在低版本中肯定是不会报错的。区别在于

SpringApplication#prepareContext,会重新设置allowCircularReferences为false。

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);this.postProcessApplicationContext(context);this.applyInitializers(context);listeners.contextPrepared(context);bootstrapContext.close(context);if (this.logStartupInfo) {this.logStartupInfo(context.getParent() == null);this.logStartupProfileInfo(context);}ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}context.addBeanFactoryPostProcessor(new SpringApplication.PropertySourceOrderingBeanFactoryPostProcessor(context));Set<Object> sources = this.getAllSources();Assert.notEmpty(sources, "Sources must not be empty");this.load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);}

AbstractAutowireCapableBeanFactory#doCreateBeanearlySingletonExposure该值为false,不会进行三级缓存。

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}

DefaultSingletonBeanRegistry#getSingleton(String, boolean),在第二次获取实例的时候,获取不到元素,调用的是三级缓存中的ObjectFactory来创建。如果allowCircularReferences为false,三级缓存为空,不会创建。

	@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}

在这里插入图片描述


http://www.ppmy.cn/news/979143.html

相关文章

28.1 kibana

Kibana 是一个免费且开放的用户界面&#xff0c;能够对 Elasticsearch 数据进行可视化操作&#xff0c;从跟踪查询负载&#xff0c;到理解请求如何流经整个应用&#xff0c;都能轻松完成。 1.Kibana安装 注意要与ES版本保持一致 https://www.elastic.co/downloads/past-relea…

Python版day57

647. 回文子串 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成&#xff0…

Flask如何使用蓝图来组织和管理应用程序的路由和视图函数?

在Flask中&#xff0c;蓝图是一种非常方便的工具&#xff0c;可以帮助我们组织和管理应用程序的路由和视图函数。从新手的角度来看&#xff0c;我们可以把蓝图想象成一个超级市场&#xff0c;所有的商品都井然有序地摆放在货架上&#xff0c;我们可以在需要的时候轻松地找到我们…

OpenWebtext数据集

paper:Aaron Gokaslan, Vanya Cohen, Pavlick Ellie, and Stefanie Tellex. Openwebtext corpus, 2019. 用于GPT2训练的文本数据,GPT2主要是生成文本的,只有足够多的数据训练的模型才能学会说话,生成的才有可能是通顺的。GPT2文本生成可以采用huggingface中代码直接进行训…

机器学习:GPT3

GPT3 模型过于巨大 GPT3是T5参数量的10倍&#xff01; 训练GPT3的代价是$12百万美元 Zero-shot Ability GPT3的思想是不是能拿掉Fine-tune 只需要给定few-shot或者zero-shot就能干相应的任务了。 few-shot learning&#xff08;no gradient descent&#xff09;&#…

Qt应用开发(基础篇)——QComboBox

目录 一、前言 二、属性和方法 三、信号 一、前言 QComboBox继承于QWidget&#xff0c;作为Qt Wdiget常用的控件&#xff0c;在实际开发中&#xff0c;经常用来作为某些特定参数属性的选择&#xff0c;比如语言、国家、字体、主题、模式、串口号、波特率等选择性…

【雕爷学编程】Arduino动手做(87)---ULN2003步进电机模组2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

Elasticsearch 映射Mappings (三)

文章目录 前言一、Mapping简介查看索引映射 二、自动映射 dynamic mapping自动类型推断mapping注意点 三、手动映射 Expllicit mapping创建索引 四、自动映射模板 Dynamic Templates定义映射模板规则判定&#xff1a;conditlonsmatch_mapping_typematch、unmatchpath_match、pa…