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

目录
  • 前言:
  • 例子
  • 启动容器
    • 加载circulationa AbstractBeanFactory
    • 最终调用BeanDefinitionValueResolver
    • circulationb加载分析

前言:

之前我们有分析过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="src.bean.CirculationA">
    <constructor-arg name="circulationB" ref="circulationb"/>
  </bean>
  <bean id="circulationb" class="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

/**
	 * Resolve a reference to another bean in the factory.
	 */
	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循环引用失败的资料请关注我们其它相关文章!

(0)

相关推荐

  • Spring Boot2.6.0新特性之默认禁止循环引用

    目录 前言 处理方案一: 处理方案二: 处理方案三: 总结 前言 如下代码,ComponentA类注入ComponentB类,ComponentB类注入ComponentA类,就会发生循环依赖的问题,在2.6.0之前,spring会自动处理循环依赖的问题 import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class ComponentA { @Resou

  • spring源码学习之bean的初始化以及循环引用

    实例化方法,把bean实例化,并且包装成BeanWrapper 1.点进这个方法里面. 这个方法是反射调用类中的 factoryMethod 方法. 这要知道@Bean 方法的原理, 实际上spring 会扫描有@bean 注解的方法, 然后把方法名称设置到 BeanDefinition 的 factoryMethod属性中, 接下来就会调到上面截图中的方法实现@Bean 方法的调用. 2. 有参构造函数的时候 determineConstructorsFromBeanPostProcessor

  • 详解在springmvc中解决FastJson循环引用的问题

    我们先来看一个例子: package com.elong.bms; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import com.alibaba.fastjson.JSON; public class Test { public static void main(String[] args) { Map<String, Student> maps = new HashMap<

  • 简单了解spring bean的循环引用

    看过一次spring公开课,记录一下bean的循环引用问题. 问题: public class IndexService{ @Autowired IndexDao indexDao; } public class IndexDao{ @Autowired IndexService indexService; } 以上的实例中IndexService依赖IndexDao,IndexDao中依赖IndexService. spring在bean的实例化过程: 先去创建IndexDao bean, 1

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

    目录 前言: 例子 启动容器 加载circulationa AbstractBeanFactory 最终调用BeanDefinitionValueResolver circulationb加载分析 前言: 之前我们有分析过Spring是怎么解决循环引用的问题,主要思路就是三级缓存: Spring在加载beanA的时候会先调用默认的空构造函数(在没有指定构造函数实例化的前提下)得到一个空的实例引用对象,这个时候没有设置任何值,但是Spring会用缓存把它给提前暴露出来,让其他依赖beanA的bea

  • Spring EnableAsync注解异步执行源码解析

    目录 概述 @EnableAsync 分析 ProxyAsyncConfiguration 分析 AsyncAnnotationBeanPostProcessor 分析 AsyncAnnotationAdvisor 分析 Advice 构建 Pointcut 构建 AnnotationAsyncExecutionInterceptor 分析 AsyncTaskExecutor 查找 Callable 任务封装 doSubmit 异步执行方法 总结 概述 基于 Spring Framework v

  • Spring源码解析之循环依赖的实现流程

    目录 前言 循环依赖实现流程 前言 上篇文章中我们分析完了Spring中Bean的实例化过程,但是没有对循环依赖的问题进行分析,这篇文章中我们来看一下spring是如何解决循环依赖的实现. 之前在讲spring的过程中,我们提到了一个spring的单例池singletonObjects,用于存放创建好的bean,也提到过这个Map也可以说是狭义上的spring容器. private final Map<String, Object> singletonObjects = new Concurr

  • spring获取bean的源码解析

    介绍 前面一章说了AbstractApplicationContext中的refresh方法中的invokeBeanFactoryPostProcessors.主要是调用BeanFactoryPostProcessor.其中也有获取bean的过程,就是beanFactory.getBean的方法.这一章就说下getBean这个方法.由于spring中获取bean的方法比较复杂,涉及到的流程也非常多,这一章就先说下整个大体的流程.其中的细节会在后面也会慢慢说. 源码 直接看源码吧 @Overrid

  • Spring源码解析后置处理器梳理总结

    目录 前言 1.InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation()方法 2.SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors()方法 3.MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition()方法 4.SmartInstantiationA

  • Spring SpringMVC在启动完成后执行方法源码解析

    关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件) 应用场景:很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用了其他类(可能是更复杂的关联),所以当我们去使用这个类做事情时发现包空指针错误,这是因为我们这个类有可能已经初始化完成,但是引用的其他类不一定初始化完成,所以发生了空指针错误,解决方案如下: 1.写一个类继承spring的ApplicationListener监听,并监控ContextRefresh

  • Spring Security源码解析之权限访问控制是如何做到的

    〇.前文回顾 在实战篇<话说Spring Security权限管理(源码详解)>我们学习了Spring Security强大的访问控制能力,只需要进行寥寥几行的配置就能做到权限的控制,本篇来看看它到底是如何做到的. 一.再聊过滤器链 源码篇中反复提到,请求进来需要经过的是一堆过滤器形成的过滤器链,走完过滤器链未抛出异常则可以继续访问后台接口资源,而最后一个过滤器就是来判断请求是否有权限继续访问后台资源,如果没有则会将拒绝访问的异常往上向异常过滤器抛,异常过滤器会对异常进行翻译,然后响应给客户端

  • Spring源码解析之编程式事务

    一.前言 在Spring中,事务有两种实现方式: 编程式事务管理: 编程式事务管理使用TransactionTemplate可实现更细粒度的事务控制.声明式事务管理: 基于Spring AOP实现.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务. 声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单(尤其是配合spring boot自动配置,可以说是精简至极!),且大部分业务都可

  • Spring源码解析 Bean的实例化

    目录 前言 准备工作 实例化bean 1.AbstractBeanFactory 的 doGetBean方法 2.AbstractAutowireCapableBeanFactory 的 createBean方法 3.AbstractAutowireCapableBeanFactory 的 doCreateBean方法 4.AbstractAutowireCapableBeanFactory 的 createBeanInstance方法 5.AbstractAutowireCapableBean

  • Spring AOP实现声明式事务机制源码解析

    目录 一.声明式全局事务 二.源码 三.小结: 一.声明式全局事务 在Seata示例工程中,能看到@GlobalTransactional,如下方法示例: @GlobalTransactional public boolean purchase(long accountId, long stockId, long quantity) { String xid = RootContext.getXID(); LOGGER.info("New Transaction Begins: " +

随机推荐