我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring循环引用失败问题源码解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring循环引用失败问题源码解析

前言:

之前我们有分析过Spring是怎么解决循环引用的问题,主要思路就是三级缓存;

Spring在加载beanA的时候会先调用默认的空构造函数(在没有指定构造函数实例化的前提下)得到一个空的实例引用对象,这个时候没有设置任何值,但是Spring会用缓存把它给提前暴露出来,让其他依赖beanA的bean可以持有它提前暴露的引用;

比如 a 依赖b ,b依赖a,并且他们都是通过默认方法实例化,那么简单流程是这样的:

  • ioc实例化a,a提前暴露自己的,然后填充属性值,在填充属性值的时候发现有个对象b,这个时候去容器里面取到b的引用,发现b还没有被创建,那么就走实例化b的流程;
  • 实例化b;流程跟a一样;但是不同的是b填充属性的时候,发现有引用a的实例,这个时候a已经提前暴露了自己了,所以b可以直接在容器里面拿到a的引用;那么b就实例化并且也初始化完成了;
  • 拿到b了之后,a就可以持有b的引用 ,整个流程就走完了;

具体详细一点可以看这篇文章Spring-bean的循环依赖以及解决方式

Spring不能解决“A的构造方法中依赖了B的实例对象,同时B依赖了A的实例对象”这类问题

这篇文章我想从源码的角度来分析一下整个流程;

并且分析一下Spring为什么不能解决“A的构造方法中依赖了B的实例对象,同时B依赖了A的实例对象”这类问题

例子

首先创建两个bean类; CirculationA 有个属性circulationB,并且有个构造函数给circulationB赋值;

public class CirculationA {
    private CirculationB circulationB;
    public CirculationA(CirculationB circulationB) {
        this.circulationB = circulationB;
    }
}

CirculationB 有个属性circulationA,然后set方法

public class CirculationB {
    private CirculationA circulationA;
    public CirculationA getCirculationA() {
        return circulationA;
    }
    public void setCirculationA(CirculationA circulationA) {
        this.circulationA = circulationA;
    }
}

SpringContextConfig.xml circulationa 用给定的构造函数实例化;

circulationb 就用默认的实例化方法(默认的空构造函数)

  <bean id="circulationa" class="class="lazy" data-src.bean.CirculationA">
    <constructor-arg name="circulationB" ref="circulationb"/>
  </bean>
  <bean id="circulationb" class="class="lazy" data-src.bean.CirculationB" >
    <property name="circulationA" ref="circulationa"/>
  </bean>

好,例子准完毕,上面的例子是 circulationa的构造函数里面有circulationb;

然后circulationb属性里面有circulationa;

启动容器

结果如下:

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationa' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationb' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationb' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationa' while setting bean property 'circulationA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationa' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationb' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationb' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationa' while setting bean property 'circulationA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
Disconnected from the target VM, address: '127.0.0.1:64128', transport: 'socket'
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:648)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at StartIOCUseDefaultListAbleBeanFactory.main(StartIOCUseDefaultListAbleBeanFactory.java:30)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationb' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationa' while setting bean property 'circulationA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1531)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1276)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
    ... 17 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
    ... 27 more
 

报错了,Spring它解决不了这种情况 Ok,源码走起来: 为了节省篇幅我只贴重要代码 第一步

加载circulationa AbstractBeanFactory

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
	protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							try {
								return createBean(beanName, mbd, args);
							}
						}
					});
				}
	}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		//在创建之前把beanName加入到正在创建中的属性中singletonsCurrentlyInCreation;
		//但是这个是一个set,如果之前已经加进去了,再进去就抛异常BeanCurrentlyInCreationException
		//Requested bean is currently in creation: Is there an unresolvable circular reference?")提示可能存在循环引用
		beforeSingletonCreation(beanName);
	}
	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
			throws BeanCreationException {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
//.......
// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				exposedObject = initializeBean(beanName, exposedObject, mbd);
			}
		}
}
	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
	Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
	//因为circulationa是有构造函数的,所以使用autowireConstructor
		if (ctors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
			return autowireConstructor(beanName, mbd, ctors, args);
		}
	}
//最终执行
public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
			Constructor<?>[] chosenCtors, final Object[] explicitArgs) {
			//解析构造函数参数值
				minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
				//......
//选择对应的策略来实例化对象;这里是生成正在的实例了。
//但是在这之前,构造参数要拿到
				beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
						mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
}
}

省略....

最终调用BeanDefinitionValueResolver


	private Object resolveReference(Object argName, RuntimeBeanReference ref) {
		try {
			String refName = ref.getBeanName();
			refName = String.valueOf(doEvaluate(refName));
			if (ref.isToParent()) {
				if (this.beanFactory.getParentBeanFactory() == null) {
					throw new BeanCreationException(
							this.beanDefinition.getResourceDescription(), this.beanName,
							"Can't resolve reference to bean '" + refName +
							"' in parent factory: no parent factory available");
				}
				//!!!这里,要先去查找refName的实例
				return this.beanFactory.getParentBeanFactory().getBean(refName);
			}
			else {
				Object bean = this.beanFactory.getBean(refName);
				this.beanFactory.registerDependentBean(refName, this.beanName);
				return bean;
			}
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					this.beanDefinition.getResourceDescription(), this.beanName,
					"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
		}
	}

跟着上面的顺序我们整理一下;

  • 启动容器,加载circulationa,因为是构造函数生成,所以要先解析构造函数的属性,这时候发现有引用circulationb,那么通过getBean(circulationb)先拿到circulationb的实例;
  • 如果拿到了,则生成circulationa的实例对象返回;但是这个时候代码执行circulationb的加载过程了;

circulationb加载分析

然后我们分析一下circulationb加载 circulationb跟circulationa差不多 加载circulationb,把它加入到正在创建的属性中

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

然后用默认的方式创建实例; circulationa 是rautowireConstructor(beanName, mbd, ctors, args)创建的;这个方法需要先拿到构造函数的值;所以执行了调用getBean(circulationb)

circulationa是调用了instantiateBean;这个方法不需要提前知道属性;它用默认的构造函数生成实例;这时候的实例是没有设置任何属性的;

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
				beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
	}

不过生成了实例之后,在doCreateBean方法中有一个populateBean;这个方法就是专门填充属性值的,因为circulationb有circulationa的属性; 所以会去容器里面取circulationa的引用;

但是circulationa这个时候还没有成功创建实例啊;因为它还一直在等circulationb创建成功之后返回给它引用呢,返回了circulationa才能创建实例啊;

这个时候circulationb没有拿到circulationa,那么又会去调用getBean(circulationa); 大家想一想如果这样下去就没完没了了啊; 所以Spring就抛出异常了 那么在哪里抛出异常呢? 在第二次调用getBean(circulationa)的时候会走到下面

		if (!this.inCreationCheckExclusions.contains(beanName) &amp;&amp; !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

因为circulationa之前加进来过一次啊,而且没有创建成功是不会删除的啊;

现在又add一次,因为this.singletonsCurrentlyInCreation是一个set;

已经存在的再次add会返回false;那么这段代码就会抛出异常了;

Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?

情况就是这样,只要是用构造函数创建一个实例,并且构造函数里包含的值存在循环引用,那么spring就会抛出异常;

所以如果有循环引用的情况请避免使用构造函数的方式

以上就是Spring循环引用失败问题源码解析的详细内容,更多关于Spring循环引用失败的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring循环引用失败问题源码解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Spring处理@Async导致的循环依赖失败问题怎么解决

本文小编为大家详细介绍“Spring处理@Async导致的循环依赖失败问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“Spring处理@Async导致的循环依赖失败问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入
2023-07-02

源码深度解析,Spring 如何解决循环依赖?

“一级缓存”的作用,变量命名为 singletonObjects,结构是 Map,它就是一个单例池,将初始化好的对象放到里面,给其它线程使用,如果没有第一级缓存,程序不能保证 Spring 的单例属性。

Java循环引用问题怎么解决

在Java中,循环引用问题通常是指两个或多个对象相互引用,导致无法被垃圾回收器回收,从而造成内存泄漏的情况。要解决循环引用问题,可以使用以下几种方法:1. 调整对象之间的引用关系:当两个对象相互引用时,可以将其中一个对象的引用改为弱引用或软
2023-10-07

Objective-C中block循环引用问题详解

目标:block执行过程中,self不会释放;执行完可以释放。 最初block中直接使用self会强引用。self.myBlock = ^() {[self doSomething]; };或者使用了对象的属性self.myBlock =
2022-06-04

JavaScript 深拷贝的循环引用问题详解

如果说道实现深拷贝最简单的方法,我们第一个想到的就是 JSON.stringify() 方法,因为JSON.stringify()后返回的是字符串,所以我们会再使用JSON.parse()转换为对象,这篇文章主要介绍了JavaScript 深拷贝的循环引用问题,需要的朋友可以参考下
2022-12-27

浅析 SpringMVC 中返回对象的循环引用问题

@RestController、@ResponseBody 等注解是我们在写 Web 应用时打交道最多的注解了,我们经常有这样的需求:返回一个对象给前端,SpringMVC 帮助我们序列化成 JSON 对象。

Spring Boot中CORS问题及解决办法,源码解析

在Spring Boot应用程序中,CORS问题可能会出现,因为浏览器会阻止来自不同源的请求。默认情况下,Spring Boot允许来自同一源的请求,但会阻止来自不同源的请求。
CORSSpring2024-11-30

解析Spring中的循环依赖问题:再探三级缓存(AOP)

当涉及Spring框架中动态代理的实现机制时,除了已经提到的earlySingletonObjects和singletonFactories这两个缓存外,还有一个重要的缓存值得一提,那就是earlyProxyReferences。这个缓存的
Spring框架AOP2024-11-30

vue源码解析computed多次访问会有死循环原理

这篇文章主要为大家介绍了vue源码解析computed多次访问会有死循环原理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-14

分析与解决HTTP状态码502:网页访问失败的问题

HTTP状态码502:网页访问失败的原因分析与解决方案概述当我们在浏览器中访问某个网页时,有时会遇到HTTP状态码502。这个状态码表示网关错误,意味着客户端请求正确,但是服务器无法提供正确的响应。本文将分析造成502错误的原因,并提供一
分析与解决HTTP状态码502:网页访问失败的问题
2024-02-23

使用JSON.stringify时遇到的循环引用问题怎么解决

这篇文章给大家分享的是有关使用JSON.stringify时遇到的循环引用问题怎么解决的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。程序员在日常做TypeScript/JavaScript开发时,经常需要将复杂的
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录