【Spring源码学习】Spring Bean实例化-循环依赖

一、什么是循环依赖

如果类A存在属性类B,而类B也有属性类A,那么当进行属性的依赖注入时,就会出现A还未完成创建,又由于在创建B的过程中又发生创建A的过程,造成了死循环,最终导致循环依赖。类似代码如下:

public class A {
    private B b;
}
 
public class B {
    private A a;
}

循环依赖只会出现在单例实例无参构造函数实例化情况下。有参构造函数的加@Autowired 的方式循环依赖是直接报错的,多例的循环依赖也是直接报错的。接下来我会通过源代码一一说明。

二、循环依赖的步骤

前面几章中,我重点跟了Spring Bean的实例化源码过程,总结一下就是:

循环依赖步骤:
1、A 类无参构造函数实例化后,设置三级缓存
2、A 类populateBean 进行依赖注入,这里触发了B 类属性的getBean 操作
3、B 类无参构造函数实例化后,设置三级缓存
4、B 类populateBean 进行依赖注入,这里触发了A 类属性的getBean 操作
5、A 类之前正在实例化,singletonsCurrentlyInCreation 集合中有已经有这个A 类了,三级缓存里面也有了,所以这
时候是从三级缓存中拿到的提前暴露的A 实例,该实例还没有进行B 类属性的依赖注入的,B 类属性为空,具体通过Object
sharedInstance = getSingleton(beanName);这个方法(先从一级缓存中拿,再从二级缓存中拿,如果还拿不到,并且允许bean
提前暴露则从三级缓存中拿到对象工厂,从工厂中拿到对象成功后,升级到二级缓存,并删除三级缓存。再有别的类引用的话
就从二级缓存中进行取)
6、B 类拿到了A 的提前暴露实例注入到A 类属性中了
7、B 类实例化已经完成,B 类的实例化是由A 类实例化中B 属性的依赖注入触发的getBean 操作进行的,现在B 已
经实例化,所以A 类中B 属性就可以完成依赖注入了,这时候A 类B 属性已经有值了。
8、B 类A 属性指向的就是A 类实例堆空间,所以这时候B 类A 属性也会有值了。

三、为什么有参构造函数的加@Autowired 的方式循环依赖是直接报错的?

首先说明一点,构造方法的执行顺序是在依赖注入属性之前的。

  1. 创建bean 的实例是在方法createBeanInstance(beanName, mbd, args)中通过无参或有注解@Autowired 参构
    造函数实例化进行的。
//AbstractAutowireCapableBeanFactory
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) {
		//创建实例,,重点看,重要程度:5
		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.
	
	//......省略部分代码
	
	// Eagerly cache singletons to be able to resolve circular references
	// even when triggered by lifecycle interfaces like BeanFactoryAware.
	//是否单例bean提前暴露
	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");
		}
		//这里着重理解,对理解循环依赖帮助非常大,重要程度 5   添加三级缓存
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		//ioc di,依赖注入的核心方法,该方法必须看,重要程度:5
		populateBean(beanName, mbd, instanceWrapper);
		//bean 实例化+ioc依赖注入完以后的调用,非常重要,重要程度:5
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}

	//......省略部分代码

	// Register bean as disposable.
	try {
		//注册bean销毁时的类DisposableBeanAdapter
		registerDisposableBeanIfNecessary(beanName, bean, mbd);
	}
	catch (BeanDefinitionValidationException ex) {
		throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
	}
	return exposedObject;
}
  1. createBeanInstance 方法执行完毕之后才会判断是否是单例bean 提前暴漏从而为实例化的bean 添加三级缓存
    singletonFactories
//是否单例bean提前暴露:单例、支持循环依赖、是正在创建的实例
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");
	}
	//这里着重理解,对理解循环依赖帮助非常大,重要程度 5   添加三级缓存
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

默认情况下,支持循环依赖,默认都是单例,而正在创建的实例条件是在AbstractAutowireCapableBeanFactory.doCreateBean()的调用方,AbstractBeanFactory.deGetBean()发起的

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;
		}
	});
	//该方法是FactoryBean接口的调用入口
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

点进去看看getSingleton().
在这里插入图片描述

protected void beforeSingletonCreation(String beanName) {
	//把beanName添加到singletonsCurrentlyInCreation Set容器中,在这个集合里面的bean都是正在实例化的bean
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
}

创建完毕后,会把提前暴露的实例存放至三级缓存中

//DefaultSingletonBeanRegistry
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);
			//删除二级缓存
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

如果有参构造函数的有@Autowired ,那么在执行createBeanInstance 方法时,参数为引用类型会再次触发getBean操作。由于之前的bean 的实例化尚未完成,三级缓存中也没法获取到,所以就继续走之前第一次实例化进入的getSingleton 方法,此方法中的beforeSingletonCreation 会校验该bean 是否正在实例化。如果是有参构造函数的有@Autowired的循环依赖的话,则这里的第二个判断为false,因为第一次类A已经在里面了,就会抛异常。

protected void beforeSingletonCreation(String beanName) {
	//把beanName添加到singletonsCurrentlyInCreation Set容器中,在这个集合里面的bean都是正在实例化的bean
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
}

public BeanCurrentlyInCreationException(String beanName) {
	super(beanName,
			"Requested bean is currently in creation: Is there an unresolvable circular reference?");
}

而对于多例的循环依赖问题,在这里就会抛异常了

//AbstractBeanFactory
//如果是scope 是Prototype的,校验是否有出现循环依赖,如果有则直接报错
if (isPrototypeCurrentlyInCreation(beanName)) {
	throw new BeanCurrentlyInCreationException(beanName);
}

如果是通过属性加@Autowired 方式进行循环依赖,在进行populateBean 属性依赖注入之前就会把bean 放入三级缓存中,从而进行populateBean 操作时触发getBean 时就可以直接从三级缓存中取就可以了。

总结下三级缓存各自的功能:

缓存说明
singletonObjects第一级缓存,存放可用的成品Bean。
earlySingletonObjects第二级缓存,存放半成品的Bean,半成品的Bean是已创建对象,但是未注入属性和初始化。用以解决循环依赖。
singletonFactories第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。

四、二级缓存能否解决循环依赖

  1. 若没有第二级缓存,若有多次依赖某一对象,则每次都需要从三级缓存中创建该对象,失去了单例的意义。(我要的就是单例,你给我来多例,逗我呢?)
  2. 若没有第三级缓存,对于一般的对象循环依赖问题可以得到解决。那么就得去改写 AbstractAutowireCapableBeanFactory 的 doCreateBean 的方法了。
//添加到三级缓存
  if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
    logger.trace("Eagerly caching bean '" + beanName +
      "' to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   //从三级缓存中取出立刻放入二级缓存
   getSingleton(beanName, true);
  }

但是,对于AOP代理对象的生成,会违背Spring总体实例化对象的流程,因为代理对象是在初始化bean完全之后,进行该bean的代理。即:
Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。

若没有第三级缓存,则创建完bean,就得立马创建代理对象,违背了Spring设计原则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值