深入分析Spring循环依赖

267 阅读4分钟

循环依赖

循环依赖其实说的很直白,就是两个类之前相互引用,比如

public class Aservice {

	Bservice bservice;

	public Bservice getBservice() {
		return bservice;
	}

	public void setBservice(Bservice bservice) {
		this.bservice = bservice;
	}
}

public class Bservice {

	Aservice aservice;

	public Aservice getAservice() {
		return aservice;
	}

	public void setAservice(Aservice aservice) {
		this.aservice = aservice;
	}
}

如果在解析依赖时,不对这种情况进行处理,就会陷入依赖死循环,在创建Aservice注入Bservice,在创建Bservice时注入Aservice,那Spring IoC是如何解决这种情况的呢?在总结IoC原理时,有简单提及过,参考juejin.cn/post/697692…

首先在org.springframework.context.support.ClassPathXmlApplicationContextTests里加一个单元测试类

@Test
public void mytest() {
	ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( PATH + "test/myContext.xml");
	assertTrue(ctx.containsBean("aservice"));
}

另外自己新加myContext.xml

<bean name="aservice" class="org.springframework.context.Aservice">
	<property name="bservice" ref="bservice"/>
</bean>

<bean name="bservice" class="org.springframework.context.Bservice">
	<property name="aservice" ref="aservice"/>
</bean>

启动mytest。

整体思路

Spring IoC主要使用3个Map作为缓存,缓存不同状态的Bean,在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

image.png

singletonObjects:存放已经实例化好的Bean

singletonFactories:存放ObjectFactory

earlySingletonObjects:存放还未实例化好的Bean,这是属于提前暴露的Bean

解决循环依赖核心的地方

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					singletonObject = singletonFactory.getObject();
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

在org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean中有两个关键的地方:


// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName); // ①
if (sharedInstance != null && args == null) {
	if (logger.isDebugEnabled()) {
		if (isSingletonCurrentlyInCreation(beanName)) {
			logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
					"' that is not fully initialized yet - a consequence of a circular reference");
		}
		else {
			logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
		}
	}
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

...

// 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;
		}
	});
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

一开始,3个map的都是空的

1、当启动单测时,执行到①处,首先从singletonObjects取aservice,此时为空,接着执行到②,进入createBean,createBean会一路走到 createBean -> doCreateBean -> addSingletonFactory,在addSingletonFactory里,singletonObjects不存在aservice,就把aservice添加进来。即下面的③处

在doCreateBean方法,也有三个关键的地方

// 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.isDebugEnabled()) {
		logger.debug("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);
}

...

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 " +
						"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
			}
		}
	}
}

2、当代码走到doCreateBean的④处时,因为Aservice依赖Bservice,所以会再次调用getBean,因为要创建Bservice,此时Bservice也放进了singletonFactories,当再次走到④处时,因为Bservice依赖Aservice,所以又再次调用getBean,然而,再次进入①处时,由于Aservice已经在singletonFactories了,所以会把Aservice加入到earlySingletonObjects,提前暴露

image.png

但是,此时的aservice依然是个未实例化完成的Bean,所以getBean在这里就返回了

3、再次回到populateBean,此时bservice依赖的aservice,已经注入了,在接下来的initializeBean,意味着bservice已经实例化完成

此时需要关注一下3个map的状态:

singletonObjects : null

singletonFactories : bservice

earlySingletonObjects : aservice

因为bservice已经实例化完成,所以代码需要回到②处的getSingleton方法,继续singletonObject = singletonFactory.getObject();的后面往下走

image.png

然后代码会走到最后的addSingleton(beanName, singletonObject)

protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		this.singletonObjects.put(beanName, singletonObject);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

当代码走到这里,此时3个map的状态

singletonObjects : bservice

singletonFactories : null

earlySingletonObjects : aservice

4、当bservice已经处理好后,代码需要回到创建aservice时,执行populateBean的位置,即④处,从上面可知,bservice已经在singletonObjects了,aservice还在earlySingletonObjects,但是有个地方需要注意的是,此时 Aservice类的bservice已经注入了(这里需要停下来,想想为什么)

因为aservice在earlySingletonObjects中,所以代码不会走进⑤处里面,而是返回到②处继续走,也就是走到最后的addSingleton(beanName, singletonObject),同第3点

当代码再次进入到addSingleton时,就会吧aservice加入到singletonObjects,并从earlySingletonObjects溢出

image.png

此时,3个map的状态

singletonObjects : bservice,aservice

singletonFactories : null

earlySingletonObjects : null

到此,已经解决了循环依赖的问题了

总结:

这个梳理工作让我觉得就是,一个字,乱。想要理清整个思路还是有点困难的,我的处理方法是: (1)理解3个map的作用

(2)在getBean里面,找到对3个map进行get和put的关键点地方

(3)梳理出自己理解的主线

image.png

这是我自己梳理的草稿

(4)对着草稿和在关键点地方debug,然后修正自己的理解

(5)同时输出博客,整理自己的思路