循环依赖
循环依赖其实说的很直白,就是两个类之前相互引用,比如
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
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,提前暴露
但是,此时的aservice依然是个未实例化完成的Bean,所以getBean在这里就返回了
3、再次回到populateBean,此时bservice依赖的aservice,已经注入了,在接下来的initializeBean,意味着bservice已经实例化完成
此时需要关注一下3个map的状态:
singletonObjects : null
singletonFactories : bservice
earlySingletonObjects : aservice
因为bservice已经实例化完成,所以代码需要回到②处的getSingleton方法,继续singletonObject = singletonFactory.getObject();的后面往下走
然后代码会走到最后的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溢出
此时,3个map的状态
singletonObjects : bservice,aservice
singletonFactories : null
earlySingletonObjects : null
到此,已经解决了循环依赖的问题了
总结:
这个梳理工作让我觉得就是,一个字,乱。想要理清整个思路还是有点困难的,我的处理方法是: (1)理解3个map的作用
(2)在getBean里面,找到对3个map进行get和put的关键点地方
(3)梳理出自己理解的主线
这是我自己梳理的草稿
(4)对着草稿和在关键点地方debug,然后修正自己的理解
(5)同时输出博客,整理自己的思路