Spring 源码(八)循环依赖
admin
2023-01-21 10:01:11
0

循环依赖是指两个或者多个Bean之前相互持有对方。在Spring中循环依赖一般有三种方式:

  1. 构造函数循环依赖

  2. setter方法循环依赖

  3. prototype 范围的依赖处理

构造函数循环依赖

在Spring中构造函数循环依赖是无法解决的,因为构造函数依赖其实是方法间循环调用的一种,会发生死循环。但是在Spring中会直接抛出BeanCurrentlyInCreationException异常。源码如下:

// 在缓存中获取Bean,如果没有就创建Beanpublic Object getSingleton(String beanName, ObjectFactory singletonFactory) {
	Assert.notNull(beanName, "'beanName' must not be null");	synchronized (this.singletonObjects) {		// 在缓存中获取Bean
		Object singletonObject = this.singletonObjects.get(beanName);		if (singletonObject == null) {			// 判断容器是否正在销毁单实例Bean
			if (this.singletonsCurrentlyInDestruction) {				throw new BeanCreationNotAllowedException(beanName,						"Singleton bean creation not allowed while singletons of this factory are in destruction " +						"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
			}			// 将当前需要创建的Bean标示放到Set集合,如果失败则抛出BeanCurrentlyInCreationException异常
			beforeSingletonCreation(beanName);			boolean newSingleton = false;			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);			if (recordSuppressedExceptions) {				this.suppressedExceptions = new LinkedHashSet();
			}			try {				// 创建Bean实例
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			...			if (newSingleton) {				// 将Bean实例注册到singletonObjects容器中
				addSingleton(beanName, singletonObject);
			}
		}		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}
}protected void beforeSingletonCreation(String beanName) {	// 将当前需要创建的Bean标示方法Set集合,如果失败则抛出BeanCurrentlyInCreationException异常
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {		throw new BeanCurrentlyInCreationException(beanName);
	}
}

执行过程:

  1. 从缓存中获取Bean,如果没有则走创建Bean流程

  2. 判断容器是否正在销毁单实例Bean,如果是则不创建Bean

  3. 将当前需要创建的Bean标示(name)放入Set集合中(当前正在创建的Bean池),如果放入失败则抛出BeanCurrentlyInCreationException异常

  4. 创建Bean实例

  5. 将Bean实例注册到容器(放到缓存中)

解决构造函数依赖主要是第3步实现的,Spring在容器创建的Bean的时候,会将Bean的标示(name)放到一个Set集合里面(当前正在创建的Bean池)。当在创建Bean的过程中,发现自已经在这个Set集合中时,就直接会抛出BeanCurrentlyInCreationException异常,而不会发生死循环。

setter方法循环依赖

@Servicepublic class AService {    @Autowired
    private BService bService;    @Autowired
    public void setbService(BService bService) {        this.bService = bService;
    }
}

这两种方式都算是setter方法依赖。我们创建单实例Bean的大致过程可以划分成三个阶段:

  1. 实例化 createBeanInstance(beanName, mbd, args);

  2. 设置属性值 populateBean(beanName, mbd, instanceWrapper);

  3. 初始化 initializeBean(beanName, exposedObject, mbd);

对于Setter注入造成的循环依赖,Spring容器是在创建Bean第一步实例化后,就将Bean的引用提前暴露出来。通过提前暴露出一个单例工厂方法,从而使得其他Bean可以引用到该Bean。

创建Bean时提前暴露刚完成第一步的Bean,源码如下:

addSingletonFactory(beanName, new ObjectFactory() {	@Override
	public Object getObject() throws BeansException {		return getEarlyBeanReference(beanName, mbd, bean);
	}
});// 将单例工厂放入缓存中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);
		}
	}
}

自动装配过程中获取单实例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 != NULL_OBJECT ? singletonObject : null);
}

我们可以看到,在自动装配Bean的过程中,会去找三个缓存:

  1. singletonObjects:存放完成创建的Bean所有步骤的单实例Bean

  2. earlySingletonObjects:存放只完成了创建Bean的第一步,且是由单实例工厂创建的Bean

  3. singletonFactories:存放只完成了创建Bean的第一步后,提前暴露Bean的单实例工厂。http://www.chacha8.cn/detail/1132398214.html

这里为什么会用一个三级缓存呢,为啥不直接将只完成了创建Bean的第一步的Bean直接方到earlySingletonObjects缓存中呢?

我们跟入 getEarlyBeanReference(beanName, mbd, bean)我们可以发现,单实例工厂方法返回Bean的时候还执行了后置处理器的,在后置处理器中我们还可以对Bean进行一些特殊处理。如果我们直接将刚完成实例化的Bean放入earlySingletonObjects缓存中,那么失去对Bean进行特殊处理的机会。

源码如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;	if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {		for (BeanPostProcessor bp : getBeanPostProcessors()) {			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);				if (exposedObject == null) {					return null;
				}
			}
		}
	}	return exposedObject;
}

对于singleton 作用域 bean ,可以通过setAllowCircularReferences(false);来禁用循环引用。郑州不育不孕医院:http://www.zzchyy110.com/

prototype 范围的依赖处理

对于prototype 作用域 bean, Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓存prototype作用域的 bean ,因此无法提前暴露一个创建中的 bean 示。


相关内容

热门资讯

德国总理:美国正在被伊朗羞辱 德国之声4月27日报道,德国总理默茨在访问一所学校时表示,在当前的持续冲突中,伊朗领导层正试图羞辱美...
理响中国|“长”歌以行,风云激... 光阴如梭,东方潮阔。这里是中国的长三角,世界的长三角。无论过去、现在还是未来,这片土地都因时代而生,...
白宫:特朗普及其国安团队开会讨... 新华社华盛顿4月27日电 美国白宫新闻秘书莱维特27日在记者会上证实,总统特朗普及其国家安全团队当天...
人民日报刊文:日本放开杀伤性武... 日本放开杀伤性武器出口推高地缘冲突风险(国际论坛)常思纯《人民日报》(2026年04月28日 第 0...
医疗保障法草案二审:明确生育保... 满足多样化健康保障需求本报记者 彭 波4月27日,医疗保障法草案二审稿提请十四届全国人大常委会第二十...
天津一景区发生自转旋翼机事故1... 澎湃新闻记者 吕新文中国民用航空华北地区管理局4月22日公布《豪客通航“10•1”天津长芦汉盐旅游区...
卡塔尔埃米尔与美国总统特朗普通... 当地时间24日,卡塔尔埃米尔塔米姆与美国总统特朗普通电话,重点就中东地区局势以及伊朗与美国谈判问题交...
男子30年前被扣押2859克黄... 澎湃新闻记者 王鑫家住辽宁省大连市的潘永嘉近日向澎湃新闻反映称,三十年前,他在大连周水子机场被盖州市...
商务部:取消反制欧盟两家金融机... 中华人民共和国商务部令二〇二六年 第1号鉴于欧盟已取消对中国两家金融机构的制裁措施,现公布《关于取消...
过去24小时共有5艘船只通过霍... 总台记者当地时间24日获悉,过去24小时内,共有5艘船只通过霍尔木兹海峡,其中包括一艘伊朗油轮。(总...