Spring核心源码-如何解决循环依赖

news/2025/3/5 0:08:46/

假设有两个类A和B
B是A的成员变量,A也是B的成员变量。
假设类A的bean为a,类B的bean为b。且IOC容器先处理A。

熟悉Spring容器初始化的同学,应该都知道,容器初始化的过程中,bean的创建是如下触发的:
在这里插入图片描述
getBean 的时候发现不存在,就去 createBean
bean的创建是在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 完成的。

doCreateBean 大致可以分为三步:

1、实例化bean:createBeanInstance
2、填充属性:populateBean
3、初始化bean:initializeBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;// 实例化beanif (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}......boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {// 放入3级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}......try {// 填充属性populateBean(beanName, mbd, instanceWrapper);// 初始化beanexposedObject = initializeBean(beanName, exposedObject, mbd);}...... return exposedObject;}

其中第三步,只是Spring框架提供的 InitializingBean 接口的扩展,用于设置完properties之后做一些动作,对循环依赖没有影响。
循环依赖的处理只发生在第一步和第二步。

从以上代码可以看到,当A类的一个单例对象a被实例化之后,被立即放在了3级缓存内,具体的代码如下:

	/*** Add the given singleton factory for building the specified singleton* if necessary.* <p>To be called for eager registration of singletons, e.g. to be able to* resolve circular references.* @param beanName the name of the bean* @param singletonFactory the factory for the singleton object*/protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {// 放入3级缓存this.singletonFactories.put(beanName, singletonFactory);// 从2级缓存中移除(确保2级缓存没有)this.earlySingletonObjects.remove(beanName);// 标记这个bean已经创建过this.registeredSingletons.add(beanName);}}}

在给a设置属性B的时候,去对B进行 getBean ,发现不存在,也会对B进行 createBean
类B的对象b,在实例化之后,也会进行属性的设置,会对类A进行 getBean ,这部分就有了差异。

@SuppressWarnings("unchecked")protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object beanInstance;// 这里去获取A的单例bean// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}

getSingleton 的代码如下:

	/*** Return the (raw) singleton object registered under the given name.* <p>Checks already instantiated singletons and also allows for an early* reference to a currently created singleton (resolving a circular reference).* @param beanName the name of the bean to look for* @param allowEarlyReference whether early references should be created or not* @return the registered singleton object, or {@code null} if none found*/@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lock// 从1级缓存拿Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 从2级缓存拿singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {// 从3级缓存拿ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();// 放入2级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 从3级缓存中移除this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}

因为前面类A执行 doCreateBean 的时候,已经放进了3级缓存,所以b在设置属性的时候,是能拿得到a的。
这里的拿到的a还仅仅是执行了实例化的,并没有设置完属性。

b在执行完第二步设置属性,第三步初始化之后,又返回到a的第二步设置属性,第三步初始化。
至此,类A的对象a和类B的对象b,都已经创建成功。

最终总结流程图如下:
在这里插入图片描述


http://www.ppmy.cn/news/1143952.html

相关文章

【线性代数及其应用 —— 第一章 线性代数中的线性方程组】-1.线性方程组

所有笔记请看&#xff1a; 博客学习目录_Howe_xixi的博客-CSDN博客https://blog.csdn.net/weixin_44362628/article/details/126020573?spm1001.2014.3001.5502思维导图如下&#xff1a; 内容笔记如下&#xff1a;

Nginx搭建Rtmp流媒体服务,并使用Ffmpeg推流

文章目录 1.rtmp流媒体服务框架图2.nginx配置3.配置nginx4.使用ffmpeg推流5.实时推摄像头流 本项目在开发板上使用nginx搭建流媒体服务&#xff0c;利用ffmpeg进行推流&#xff0c;在pc上使用vlc media进行拉流播放。 1.rtmp流媒体服务框架图 2.nginx配置 下载&#xff1a;wge…

阿里云使用https获取git地址注意事项

首先是使用账号密码登录阿里云&#xff0c;这个账号可以使用手机号或者第三方账号注册。登录之后去下图所示地方复制 https 地址。进行拉取代码 使用https拉取代码时候&#xff0c;会让你重新输入一个阿里云的账号密码。如果是重新注册的账号&#xff0c;切记需要先去设置一下h…

问题:remote: HTTP Basic: Access denied

参看文章&#xff1a;https://baijiahao.baidu.com/s?id1740126019873950482&wfrspider&forpc 解决方法一 (最有效) 输入&#xff1a;git config --system --unset credential.helper 再次进行 Git 操作&#xff0c;输入正确的用户名&#xff0c;密码即可。

使用Pyhton执行JavaScript-pyexecjs

安装 pip install pyexecjs使用案例 import execjs print(execjs.eval("abc zxc".split(" ")))# 调用变量名 text execjs.compile(open(rtext.js).read()) print(text.eval(d))执行call function # text.js 文件 var t 1; function add(a, b) {return a…

基于AlexNet深度学习网络的智能垃圾分类系统matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、基于AlexNet深度学习网络的智能垃圾分类系统概述 4.2、基于AlexNet深度学习网络的智能垃圾分类系统主要原理 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab20…

前端笔记:Create React App 初始化项目的几个关键文件解读

1 介绍 Create React App 是一个官方支持的方式&#xff0c;用于创建单页应用的 React 设置用于构建用户界面的 JAVASCRIPT 库主要用于构建 UI 2 项目结构 一个典型的 Create React App 项目结构如下&#xff1a; ├── package.json ├── public # 这…

Acwing.4382 快速打字(双指针)

题目 芭芭拉是一个速度打字员。 为了检查她的打字速度&#xff0c;她进行了一个速度测试。 测试内容是给定她一个字符串 I&#xff0c;她需要将字符串正确打出。 但是&#xff0c;芭芭拉作为一个速度打字员&#xff0c;在追求速度的同时&#xff0c;难免会发生一些错误&…