Spring源码分析-05-循环依赖

image-20210426150337101

本文主要是分析了Spring 是如何通过属性注入的方式解决循环依赖这个问题, 参考源码给出Spring生成 BeanA 对象的流程图(严格意义上讲不算是流程图). 建议读者在阅读本文时关注singletonObjects, singletonFactories, earlySingletonObjects 这三个对象.

PS: 本文需要读者对Spring 的启动流程, getBean()流程,以及创建对象creatBean()有一个大致的了解,若缺少前提知识,请出门左转仔细阅读笔者Spring系列文章.

  • PS: 若文章字体偏大或者偏小,建议通过 ctrl键+鼠标滑轮 进行修改,以提升阅读效果.(带来不便,请谅解!)

    Version:

    • jdk 1.8
    • Spring: 5.1.7

测试案例:

循环依赖:

  • 简单来讲: A中有B,B中有A ,两者相互引用, 即所谓的循环依赖问题.

A.b=B ,B.a=A 这个样子的案例

测试类:

public class TestCircularReference {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(MainOfCircularReference.class);
        BeanA beanA = context.getBean(BeanA.class);
        BeanB beanB = context.getBean(BeanB.class);
        System.out.println("A: "+beanA.toString()+"./n B: "+beanB.toString());
        System.out.println("=====================A.b=B?  ");
        System.out.println(beanA.getBeanB());
    }
}

BeanA&&BeanB

  • BeanA.class
@Component
public class BeanA {
    public BeanB getBeanB() {
        return this.beanB;
    }
    public void setBeanB(BeanB beanB) {
        beanB = beanB;
    }
    public BeanA() {
    }
        @Autowired
    private BeanB beanB;
    public BeanA(BeanB beanB) {
        beanB = beanB;
    }
}
  • BeanB.class
@Component
public class BeanB {
    @Autowired
    private BeanA beanA;
    public BeanB(BeanA a) {
    }
    public BeanB() {
    }
    public BeanA getBeanA() {
        return beanA;
    }
    public void setBeanA(BeanA beanA) {
        this.beanA = beanA;
    }
}

测试案例:

构造器注入属性: Spring 不会解决这个问题, 会抛Exception

测试配置类:

  • MainOfCircularReference.class
@Configuration
@ComponentScan("circular.reference")
public class MainOfCircularReference {

}

测试结果:

人话描述流程:

PS:这部分内容为了贴近程序运行流程,讲的有点乱,可以选择去看总结中我给出的流程,相对清晰一点点.

Spring IOC 创建BeanA流程如下:


  1. 尝试通过缓存获取BeanAgetSingleton(“BeanA,true) ,从singletonObjects, earlySingletonObjects, singletonFactories 获取

  2. 创建BeanAdoCreateBean()

    1. 实例化beanA 即 createBeanInstance()
    2. 为beanA设置属性 ,即 BeanA.beanBpopuplateBean()
      1. 通过缓存获取 BeanB , 即getSIngleton()
      2. 创建BeanB 即 doCreateBean()
        1. 实例化beanB , 即 createBeanInstance()
        2. 为beanB设置属性即BeanB.beanA , populateBean()
          1. 通过缓存获取beanA, 并设置为beanB的属性值
        3. 初始化BeanB, 即initializeBean()
        4. 返回 beanB 对象
      3. 将BeanB 加入缓存 singletonObjects中.
      4. 返回beanB
    3. 初始化 beanA , 即initializeBean()
    4. 通过二级缓存 获取beanA 并返回.即
      Object earlySingletonReference = getSingleton(beanName, false);
  3. BeanA 加入缓存 即 singletonObjects中. addSingleton()

  4. 返回beanA.


看完上述流程, 是不是有一种我是谁,我在那,我在干什么的感觉, 这其实很正常(wuwuwu…). 以上内容虽然很多,但我们需要重点关注的内容也就几个内容. 在图中,笔者通过深色图案进行表述. 所以请一定要看图.

重点来了: 希望 读者在阅读本文时, 一定要看图, 笔者认为自己较为清晰的给出了整个调用栈的流程. 重点关注深色图案内容.

三级缓存:

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

通过判断缓存内容的变化来加深自己对Spring 解决循环依赖的理解.

image-20210426154316656

源码分析:

  • 在源码解析过程中,笔者只会给出自己认为重要的部分,读者在阅读本文的过程中,最好是实时debug ,并实时追踪 singletonObjects, earlySingletonObjects,singletonFactories, 以及singletonObjects.containsKey(“beanA”),singletonObjects.containsKey(“beanB”) 这几个值.

跳过 refresh()-> finishBeanFactoryInitialization()->getBean()-> doGetBean(XXX) 内容,从 doGetBean(“beanA”) 开始分析.

创建BeanA 对象

实例化BeanA

image-20210426154714024

  • doGetBean()->doCreateBean()->createBeanInstance()->addSIngletonFactory()

不重要跳过!

执行结果

image-20210426155207240

BeanA属性赋值

  • **即 设置BeanA.beanB属性值, 这个过程非常重要!!! ** .

image-20210426155338601

有图可以知道, 要为beanA设置属性值BeanB, 因为BeanB 未创建,所以走

  1. doGetBean()->doCreateBean()->createBeanInstance()->addSingletonFactory() 实例化beanB

  2. beanB属性属性赋值BeanB.beanA, 走 populateBean()

实例化beanB

同实例化BeanA, 跳过部分内容,直到为beanB属性赋值,即 populateBean()过程.

BeanB属性赋值

image-20210426160319879

  • DefaultSingletonBeanRegistry.class
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);// 从单例bean集合中获取(缓存)
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 判断当前bean是否正在创建
			synchronized (this.singletonObjects) {// 如果一级缓存没有,并且当前bean正在被创建
				singletonObject = this.earlySingletonObjects.get(beanName);// 尝试从二级缓存 earlySingletonObject中获取bean
				if (singletonObject == null && allowEarlyReference) { // 二级缓存中没有, 尝试从三级缓存 singletonFactories(FactoryBean)中获取
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) { // 如果三级缓存中有,那么 就把bean放入二级缓存,并在三级缓存中删除
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);// 放入二级缓存
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

由于 此过程视为beanB 设置属性值 –BeanB.beanA , 因为之前beanA 已存在singletonFactories中所以,本次对beanA调用doGetBean() 会获取到beanA,根据源码可以得知 beanA 会被放入到earlySingletonObjects中.

  • 运行结果如图:
  • image-20210426160806303
生成beanB并返回

此过程包括了,initializeBean()->addSingleton() [将BeanB加入到singletonObjects中]->return beanB

生成beanA并返回

执行完populateBean()方法后, 缓存 信息为:

image-20210426161246459

由图知:

beanB 加入到singletonObjects中(DefaultSingletonBeanRegistry.addSingleton()),

beanA加入到earlySingletonObjects中(AbstractBeanFactory.getSingleton(beanName))

  • doCreateBean() 代码片段:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
		throws BeanCreationException {
// 3. 加入singletonFactories
	// 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.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}//添加单例工厂, 已创建 单例bean
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	//3. 初始化单例bean
	//3. Initialize the bean instance.
	Object exposedObject = bean;
	try {// 赋值 bean 1. ibp.postProcessAfterInstantiation(); 2. ibp.postProcessProperties()
		populateBean(beanName, mbd, instanceWrapper);
		exposedObject = initializeBean(beanName, exposedObject, mbd);//1. postProcessBeforeInit...();-> init() ;-> afterinit..()
	}
	catch (Throwable ex) {
		if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
			throw (BeanCreationException) ex;
		}
		else {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
		}
	}
	//  处理循环依赖
	if (earlySingletonExposure) {//尝试从二级缓存earlySingletonObjects中获取bean,找不到,就返回exposedObject
		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.");
				}
			}
		}
	}
	// 4. 注册销毁方法
	// 4. Register bean as disposable.
	try {
		registerDisposableBeanIfNecessary(beanName, bean, mbd);
	}
	catch (BeanDefinitionValidationException ex) {
	}
	return exposedObject;
}

image-20210426161717469

根据图示:本段内容重点是 getSingleton(beanName,false), 阅读源码后,会发现,getSingleton(beanName,false) 的情况下,只会在singletonObjects 以及earlySingletonObjects 中根据beanname 找目标对象-beanA.

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);// 从单例bean集合中获取(缓存)
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 判断当前bean是否正在创建
		synchronized (this.singletonObjects) {// 如果一级缓存没有,并且当前bean正在被创建
			singletonObject = this.earlySingletonObjects.get(beanName);// 尝试从二级缓存 earlySingletonObject中获取bean
			if (singletonObject == null && allowEarlyReference) { // 二级缓存中没有, 尝试从三级缓存 singletonFactories(FactoryBean)中获取
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) { // 如果三级缓存中有,那么 就把bean放入二级缓存,并在三级缓存中删除
					singletonObject = singletonFactory.getObject();
					this.earlySingletonObjects.put(beanName, singletonObject);// 放入二级缓存
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

image-20210426161246459

  • 根据populateBean 结果图来看:

beanA在earlySingletonObjects中, 所以可以找到.

返回结果只BeanA.

将BeanA 加入到缓存singletonObjects中, 结束.

一个问题

Spring 为什么需要三级缓存? 而不是两级缓存?

通过本篇文章得知, Spring 在处理循环依赖时 ,只是用到了 singletonFactories, earlySingletonObjects 这两个缓存, 而singletonObjects 只是用来存放最终结果,显然这部分并未用到. 所以**,Spring 在处理循环依赖时, 真正工作的只有两个缓存**,singletonObjects 用处是用来存储已处理过, 初始化, AOP代理后(有AOP代理)的单例对象.

建议阅读: here

总结:

  • Spring 为解决循环依赖采用了三级缓存的策略, 分别是 singletonObjects, earlySingletonObjects, 以及singletonFactories,

  • 整体流程的简单描述:

    1. 实例化beanA, 并beanA 先放入 singletonFactories中
    2. BeanA 属性赋值, 即设置 BeanA.beanB
      1. 实例化beanB, 放入singletonFactories中
      2. BeanB属性赋值, 即设置BeanB.beanA
        • 通过singletonFactories 获取beanA , 将beanA 加入到 earlySingletonObjects 中
        • 初始化beanB, 尝试通过二级缓存获取beanB (失败),
        • 返回beanB, 并将beanB加入到一级缓存–singletonObjects, 清空其他缓存中的beanB对象.
      3. 初始化beanA ,尝试通过二级缓存获取beanA (成功)
      4. 返回beanA ,将beanA 加入一级缓存, 并清空其他缓存中的beanA对象.

参考:

  • https://mp.weixin.qq.com/s?__biz=MzAxNjM2MTk0Ng==&mid=2247490824&idx=1&sn=9c35fd9d593adf8fdb2cfaf5e07d7344&scene=21#wechat_redirect
  • https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans