文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring循环依赖实现过程揭秘

2023-01-13 12:02

关注

概述

我们在日常的技术交流中经常会提到Spring循环依赖,听起来挺高大尚的,那Spring到底是如何实现的呢?下面我们就来一一揭秘。

什么是循环依赖

如上图所示,A对象中包含B对象的引用,同时B对象中包含A对象的引用;也就是在创建A的过程中需要同时创建B对象,这就是所谓的循环依赖。

下面是普通对象创建的流程图,也正是循环依赖产生的根因。

可以看到A对象创建依赖B对象创建,B对象创建依赖A对象创建,形成了闭环,造成死循环了。

Spring三级缓存介绍

上图形成了一个闭环,如果想解决这个问题,那么就必须保证不会出现第二次创建A对象这个步骤,也就是说从容器中获取A的时候必须要能够获取到。

Spring中的如何解决的呢?

在Spring中,对象的创建可以分为实例化和初始化,实例化好但未完成初始化的对象是可以直接给其他对象引用的,所以此时可以做一件事,把完成实例化但未初始化的对象提前暴露出去,让其他对象能够进行引用,就完成了这个闭环的解环操作。

这就是常说的提前暴露对象。

spring中的三级缓存分别是什么

在spring源码的DefaultSingletonBeanRegistry.java类中


private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);

三级缓存解决循环依赖流程

示例

@Component
public class A {
    @Autowired
    private B b;
    public B getB() {
        return b;
    }
    public void setB(B b) {
        this.b = b;
    }
}
@Component
public class B {
    @Autowired
    private A a;
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
}
@Configuration
@ComponentScan(basePackages = "com.mashibing.selfcycle")
public class CycleConfig {
}
public class TestCycle {
    public static void main(String[] args) {
        final AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(CycleConfig.class);
        ac.close();
    }
}

关键代码

1、添加A对象工厂到三级缓存

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// 为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
// bean是实例化后的bean对象
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	// 默认最终公开的对象是bean,通过createBeanInstance创建出来的普通对象
	Object exposedObject = bean;
	// mbd的systhetic属性:设置此bean定义是否是"synthetic",一般是指只有AOP相关的pointCut配置或者Advice配置才会将 synthetic设置为true
	// 如果mdb不是synthetic且此工厂拥有InstantiationAwareBeanPostProcessor
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		// 遍历工厂内的所有后处理器
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			// 如果bp是SmartInstantiationAwareBeanPostProcessor实例
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				// 让exposedObject经过每个SmartInstantiationAwareBeanPostProcessor的包装
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
}

2、填充属性b,从beanFactory中获取b对象

	public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
			throws BeansException {
		return beanFactory.getBean(beanName);
	}

3、添加B对象工厂到三级缓存

源代码流程与《1、添加A对象工厂到三级缓存,A的实例化对象到二级缓存》 一致。

4、添加B对象的属性a,从beanFactory中获取a对象

DefaultSingleBeanRegistry.java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// Quick check for existing instance without full singleton lock
	// 从单例对象缓存中获取beanName对应的单例对象
	Object singletonObject = this.singletonObjects.get(beanName);
	// 如果单例对象缓存中没有,并且该beanName对应的单例bean正在创建中
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		//从早期单例对象缓存中获取单例对象(之所称成为早期单例对象,是因为earlySingletonObjects里
		// 的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作)
		singletonObject = this.earlySingletonObjects.get(beanName);
		// 如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用
		if (singletonObject == null && allowEarlyReference) {
			// 如果为空,则锁定全局变量并进行处理
			synchronized (this.singletonObjects) {
				// Consistent creation of early reference within full singleton lock
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					singletonObject = this.earlySingletonObjects.get(beanName);
					if (singletonObject == null) {
						// 当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
							// 如果存在单例对象工厂,则通过工厂创建一个单例对象
							singletonObject = singletonFactory.getObject();
							// 记录在缓存中,二级缓存和三级缓存的对象不能同时存在
							this.earlySingletonObjects.put(beanName, singletonObject);
							// 从三级缓存中移除
							this.singletonFactories.remove(beanName);
						}
					}
				}
			}
		}
	}
	return singletonObject;
}

5、添加B对象到一级缓存,同时删除二级、三级缓存

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton

	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			// 将映射关系添加到单例对象的高速缓存中
			this.singletonObjects.put(beanName, singletonObject);
			// 移除beanName在单例工厂缓存中的数据
			this.singletonFactories.remove(beanName);
			// 移除beanName在早期单例对象的高速缓存的数据
			this.earlySingletonObjects.remove(beanName);
			// 将beanName添加到已注册的单例集中
			this.registeredSingletons.add(beanName);
		}
	}

6、A对象赋值b属性,添加到一级缓存,完成A对象的创建 流程图

理论上使用二级缓存就能解决上面的循环依赖问题,为什么要三级缓存呢?

三级缓存存放的是objectFactory,并不是bean对象,也就是说是用来创建bean对象的。 对于动态代理对象(比如切面切到的类),需要使用三级缓存来生成代理对象。 比如,如果A,B都被切点切中了:

1、A创建时首先生成A的实例化对象a,a对象的ObjectFactory放入三级缓存。

2、填充A对象的属性b时,先实例化B对象b,b对象的ObjectFactory放入三级缓存。

3、b对象填充a属性时,如果发现a需要代理,根据三级缓存生成a的代理对象a1。

4、b对象通过a1填充a属性,在initialBean的时候走的beanPostProcessor方法是,生成b的代理对象b1,同时b1的a属性是a1。

5、a对象从一级缓存获取b1,并填充b属性。

6、a对象在initialBean的时候走的beanPostProcessor方法是,从二级缓存获取a1对象,同时a的b属性是a1。

7、最后一级缓存中存放的就是a1,b1;a1的b属性是b1,b1的a属性是a1。

到此这篇关于Spring循环依赖实现过程揭秘的文章就介绍到这了,更多相关Spring循环依赖内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯