Spring面试必问:手写Spring IoC 循环依赖底层源码剖析

简介: 在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。

概述

在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。

功能点

  1. 循环依赖的定义:两个或多个Bean相互依赖,形成闭环。
  2. Spring的解决方案:通过三级缓存机制解决循环依赖问题。
  3. 源码实现:手写核心代码,展示Spring如何解决循环依赖。

背景

在Spring中,获取一个单例Bean需要经过从缓存中查询、创建对象、依赖注入、将对象放入单例Bean缓存等步骤。当没有循环依赖时,这个流程运行得很好。但一旦出现循环依赖,就会出现问题。例如,Bean C在依赖注入Bean B时,缓存中没有Bean B(因为此时Bean B还没有完成创建),只能创建Bean B,从而导致重复创建(因为此时Bean B已经在创建中)。

业务点

Spring通过引入三级缓存机制来解决循环依赖问题:

  1. 一级缓存(singletonObjects):存储创建完毕的单例Bean。
  2. 二级缓存(earlySingletonObjects):存储已实例化但未完成创建的单例Bean。
  3. 三级缓存(singletonFactories):存储单例Bean对应的ObjectFactory,用于生成Bean实例。

底层原理

三级缓存的作用

  • singletonObjects:缓存完整的Bean对象。
  • earlySingletonObjects:缓存早期的Bean对象,即Bean的生命周期还未完全走完,但已经可以提前暴露给其他Bean使用。
  • singletonFactories:缓存ObjectFactory,用于在需要时创建Bean实例。

解决循环依赖的流程

  1. 创建Bean实例:通过反射调用构造方法创建Bean的原始对象。
  2. 判断是否存在循环依赖:如果Bean正在创建中,则存在循环依赖。
  3. 将Bean实例放入三级缓存:创建一个ObjectFactory并放入singletonFactories中,ObjectFactory的getObject方法会返回Bean的实例。
  4. 依赖注入:在依赖注入过程中,如果发现依赖的Bean不存在于一级缓存和二级缓存中,则从三级缓存中获取ObjectFactory并创建Bean实例。
  5. 将Bean实例放入二级缓存:如果Bean实例是通过ObjectFactory创建的,则将其放入earlySingletonObjects中。
  6. 完成Bean的生命周期:包括属性填充、AOP代理生成等步骤。
  7. 将Bean实例放入一级缓存:最终将完整的Bean对象放入singletonObjects中。

源码实现

下面是一个简化的源码实现,用于展示Spring如何解决循环依赖问题:

java复制代码
public class DefaultSingletonBeanRegistry {
// 一级缓存
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 HashMap<>(16);
// 判断Bean是否正在创建中
private final Set<String> singletonsCurrentlyInCreation = new HashSet<>(16);
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
                }
            }
        }
return singletonObject;
    }
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
    }
public void beforeSingletonCreation(String beanName) {
this.singletonsCurrentlyInCreation.add(beanName);
    }
public void afterSingletonCreation(String beanName) {
this.singletonsCurrentlyInCreation.remove(beanName);
this.singletonObjects.put(beanName, this.earlySingletonObjects.remove(beanName));
    }
}

应用实践

示例1:基本循环依赖

java复制代码
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}

在这个例子中,A和B相互依赖,Spring通过三级缓存机制能够成功解决循环依赖问题。

示例2:AOP代理下的循环依赖

java复制代码
@Component
public class A {
@Autowired
private B b;
}
@Component
@Aspect
public class B {
@Autowired
private A a;
}

在这个例子中,B是一个切面,Spring会在创建B的代理对象时处理循环依赖问题。

优缺点

优点

  • 简化依赖管理:通过IoC容器管理Bean的依赖关系,降低了代码耦合度。
  • 提高可测试性:方便使用Mock对象进行单元测试。
  • 解决循环依赖:通过三级缓存机制有效解决了循环依赖问题。

缺点

  • 性能损耗:三级缓存机制增加了Bean的创建成本。
  • 复杂性增加:对于开发者来说,理解三级缓存机制需要一定的学习成本。

总结

通过本文的深入剖析,相信你对Spring IoC循环依赖的底层源码有了全新的认识。Spring通过三级缓存机制巧妙地解决了循环依赖问题,使得开发者能够更加方便地管理Bean的依赖关系。同时,我们也看到了Spring在解决复杂问题时所展现出的智慧和优雅。

相关文章
|
7天前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
23天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
27天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
18天前
|
并行计算 前端开发 物联网
全网首发!真·从0到1!万字长文带你入门Qwen2.5-Coder——介绍、体验、本地部署及简单微调
2024年11月12日,阿里云通义大模型团队正式开源通义千问代码模型全系列,包括6款Qwen2.5-Coder模型,每个规模包含Base和Instruct两个版本。其中32B尺寸的旗舰代码模型在多项基准评测中取得开源最佳成绩,成为全球最强开源代码模型,多项关键能力超越GPT-4o。Qwen2.5-Coder具备强大、多样和实用等优点,通过持续训练,结合源代码、文本代码混合数据及合成数据,显著提升了代码生成、推理和修复等核心任务的性能。此外,该模型还支持多种编程语言,并在人类偏好对齐方面表现出色。本文为周周的奇妙编程原创,阿里云社区首发,未经同意不得转载。
11735 12
|
12天前
|
人工智能 自然语言处理 前端开发
100个降噪蓝牙耳机免费领,用通义灵码从 0 开始打造一个完整APP
打开手机,录制下你完成的代码效果,发布到你的社交媒体,前 100 个@玺哥超Carry、@通义灵码的粉丝,可以免费获得一个降噪蓝牙耳机。
5399 14
|
19天前
|
人工智能 自然语言处理 前端开发
用通义灵码,从 0 开始打造一个完整APP,无需编程经验就可以完成
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。本教程完全免费,而且为大家准备了 100 个降噪蓝牙耳机,送给前 100 个完成的粉丝。获奖的方式非常简单,只要你跟着教程完成第一课的内容就能获得。
9611 15
|
1月前
|
缓存 监控 Linux
Python 实时获取Linux服务器信息
Python 实时获取Linux服务器信息
|
17天前
|
人工智能 自然语言处理 前端开发
什么?!通义千问也可以在线开发应用了?!
阿里巴巴推出的通义千问,是一个超大规模语言模型,旨在高效处理信息和生成创意内容。它不仅能在创意文案、办公助理、学习助手等领域提供丰富交互体验,还支持定制化解决方案。近日,通义千问推出代码模式,基于Qwen2.5-Coder模型,用户即使不懂编程也能用自然语言生成应用,如个人简历、2048小游戏等。该模式通过预置模板和灵活的自定义选项,极大简化了应用开发过程,助力用户快速实现创意。
|
5天前
|
机器学习/深度学习 人工智能 安全
通义千问开源的QwQ模型,一个会思考的AI,百炼邀您第一时间体验
Qwen团队推出新成员QwQ-32B-Preview,专注于增强AI推理能力。通过深入探索和试验,该模型在数学和编程领域展现了卓越的理解力,但仍在学习和完善中。目前,QwQ-32B-Preview已上线阿里云百炼平台,提供免费体验。
|
13天前
|
人工智能 C++ iOS开发
ollama + qwen2.5-coder + VS Code + Continue 实现本地AI 辅助写代码
本文介绍在Apple M4 MacOS环境下搭建Ollama和qwen2.5-coder模型的过程。首先通过官网或Brew安装Ollama,然后下载qwen2.5-coder模型,可通过终端命令`ollama run qwen2.5-coder`启动模型进行测试。最后,在VS Code中安装Continue插件,并配置qwen2.5-coder模型用于代码开发辅助。
917 5