【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的实例化源码过程,总结一下就是:

循环依赖步骤:

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


文章作者: Kezade
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kezade !
评论
  目录