Spring IOC 容器原理详解:控制反转、依赖注入、Bean 生命周期
IOC控制反转是 Spring 框架的基石SpringAOP、Spring 事务、SpringBoot 自动配置全部建立在 IOC 容器之上。很多后端开发者每天都在写Service、Autowired但对 IOC 底层运行机制、Bean 完整生命周期、循环依赖的底层解决方案理解很浅。本文从设计思想、核心接口、依赖注入方式、Bean 完整生命周期、循环依赖源码思路完整拆解 IOC 容器看完彻底吃透 Spring IOC。前言Spring 两大核心支柱IOC 容器与AOP 面向切面编程。 在没有 Spring 的年代我们创建对象需要手动new对象之间的依赖关系硬编码写死。如果要替换一个实现类需要修改多处代码耦合度极高单元测试也很难 Mock 依赖对象。IOC 思想的出现就是为了解决对象耦合问题把对象的创建、销毁、依赖装配、生命周期管理全部交给 Spring 容器。开发者不再主动去 new 对象控制权交给容器这就是控制反转 Inversion of Control。 而DI依赖注入是实现 IOC 思想的具体手段。总结IOC 是思想DI 是落地实现方式。一、IOC 核心概念控制反转 依赖注入 DI1.1 什么是控制反转IOC传统开发方式开发者主动控制。// 自己new对象自己维护依赖 UserService userService new UserService(); UserDao userDao new UserDao(); userService.setUserDao(userDao);对象创建、依赖装配的控制权掌握在程序员代码里。IOC 模式容器控制。 我们只需要告诉 Spring哪些类需要交给容器管理Component/Service/Repository/Controller由 Spring 容器扫描类、实例化对象、自动装配依赖开发者直接从容器获取 Bean 使用。 对象生命周期的控制权从开发者反转到 Spring 容器。1.2 依赖注入 DIDependency Injection容器自动把当前 Bean 所需要的依赖对象注入进来不需要手动实例化依赖。Spring 提供 3 种主流注入方式构造器注入Service public class UserService{ private final UserDao userDao; //构造器注入 public UserService(UserDao userDao){ this.userDao userDao; } }优点依赖不可变、保证依赖不为空方便单元测试Spring4.3 之后单构造器无需加Autowired。Setter 方法注入Autowired public void setUserDao(UserDao userDao){ this.userDao userDao; }适合依赖可选、运行时动态修改依赖场景。字段注入Autowired 直接写在成员变量上Autowired private UserDao userDao;写法简洁但缺点明显无法声明为 final、不能脱离 IOC 容器做单元测试不推荐生产项目大量使用。开发规范建议强制依赖使用构造器注入可选依赖使用 Setter 注入尽量避免字段注入。二、IOC 容器核心接口体系Spring IOC 容器顶层接口是BeanFactoryApplicationContext是 BeanFactory 的子接口也是我们开发中最常用的容器。BeanFactorySpring 最底层 IOC 容器懒加载 Bean只有调用getBean()的时候才会实例化对象。只提供基础 Bean 管理能力功能比较单薄。ApplicationContext应用上下文BeanFactory 的增强子接口我们项目实际使用的容器。启动时预实例化单例 Bean非懒加载容器启动就能提前发现 Bean 创建异常额外能力资源加载、国际化、事件发布机制、AOP 支持、环境变量解析 常见实现类ClassPathXmlApplicationContextxml 配置方式AnnotationConfigApplicationContext注解驱动SpringBoot 底层就是这个面试考点BeanFactory 和 ApplicationContext 区别 BeanFactory 懒加载ApplicationContext 启动时加载单例 Bean功能更丰富。ApplicationContext 继承 BeanFactory。三、BeanDefinitionBean 的元数据模型Spring 不会直接拿着 Class 类去创建对象而是先把被注解标记的类解析成BeanDefinition。 BeanDefinition 是 Bean 的 “描述模板”保存类的全部信息class 全限定名作用域singleton 单例 /prototype 原型懒加载标识初始化方法、销毁方法依赖信息流程扫描类 → 生成 BeanDefinition → 根据 BeanDefinition 实例化 Bean。 可以理解BeanDefinition 是图纸Bean 是根据图纸造出来的实例。四、Bean 完整生命周期完整生命周期分为实例化 → 属性填充 → 初始化 → 使用 → 销毁资源扫描与解析容器启动扫描指定包下带有Component等注解的类解析生成 BeanDefinition存入 BeanDefinitionMap。实例化 Instantiation通过 Java 反射机制调用构造方法new 出原始对象。此时仅仅是一块空对象成员变量还没有赋值。属性填充 PopulateBeanDI 依赖注入Spring 解析Autowired等注解从容器找到依赖 Bean给对象的成员变量赋值完成依赖注入。循环依赖就发生在这一步。Aware 接口回调感知容器如果 Bean 实现了一系列 Aware 接口容器回调方法让 Bean 拿到容器资源BeanNameAware拿到 Bean 在容器中的名字BeanFactoryAware拿到 BeanFactory 对象ApplicationContextAware拿到 ApplicationContext 上下文最常用BeanPostProcessor 前置处理Bean 后置处理器在初始化方法执行之前执行 BeanPostProcessor#postProcessBeforeInitialization。AOP 代理对象就是在这里判断并生成代理。初始化 Initialization按顺序执行 ①PostConstruct注解标记方法Java 规范 ② InitializingBean#afterPropertiesSet () 接口回调 ③ xml 配置的 init-method 自定义初始化方法BeanPostProcessor 后置处理执行 postProcessAfterInitialization。重点AOP 如果需要创建代理对象就在这一步完成。如果需要代理返回代理对象不需要代理返回原始对象。单例 Bean 放入单例池 singletonObjects初始化完成的完整 Bean存入ConcurrentHashMap单例池对外提供使用。singletonObjects 就是我们常说的一级缓存。Bean 使用阶段Bean 已经就绪业务代码可以直接注入、调用。销毁 Destruction容器关闭时执行销毁逻辑仅单例 Bean 会由容器管理销毁原型 Bean 容器不管理销毁 执行顺序 ①PreDestroy注解方法 ② DisposableBean#destroy () 接口回调 ③ xml 配置 destroy-method 自定义销毁方法记忆顺序实例化 → 填充属性 → Aware 回调 → 前置后置处理器 → PostConstruct → InitializingBean → init-method → 放入单例池使用 → 销毁。五、Bean 作用域详解Spring 支持多种 Bean 作用域面试常问前两个singleton默认单例。整个 IOC 容器只创建 1 个 Bean 实例存入单例池。容器启动创建非懒加载。prototype原型。每次调用getBean()都会新建一个对象容器不管理原型 Bean 的销毁。requestWeb 环境一次 http 请求一个实例sessionWeb 环境一个 session 一个实例applicationWeb 环境全局 ServletContext 级别单例坑点单例 Bean 中如果注入原型 Bean原型 Bean 不会每次都新建因为注入只发生一次需要通过 ApplicationContext 手动 getBean 获取原型实例。六、循环依赖底层原理6.1 什么是循环依赖A 类依赖 BB 类同时依赖 A。容器创建 A 的时候发现需要 B去创建 B创建 B 的时候发现需要 AA 还没完全创建完成。Component public class A{ Autowired private B b; } Component public class B{ Autowired private A a; }6.2 Spring 如何解决单例 setter 注入循环依赖三级缓存Spring 用三级缓存解决单例、setter 注入场景下的循环依赖//一级缓存存放完全初始化好可以直接使用的单例Bean private final MapString, Object singletonObjects new ConcurrentHashMap(); //二级缓存实例化完成但还没填充属性的早期Bean对象半成品Bean private final MapString, Object earlySingletonObjects new HashMap(); //三级缓存Bean工厂用来生成早期对象引用支持AOP代理 private final MapString, ObjectFactory? singletonFactories new HashMap();执行流程创建 A实例化得到原始对象开始填充属性发现依赖 B开始创建 B实例化原始对象填充属性发现依赖 AA 还没完成初始化但是 A 实例化已经完成把 A 的 ObjectFactory 放入三级缓存 singletonFactoriesB 去三级缓存拿到 A 的早期引用半成品 A完成 B 的属性填充、初始化B 完整创建完成放入一级缓存回到 A注入 B 对象A 完成属性填充初始化A 移入一级缓存删除二级、三级缓存中 A 的临时记录。6.3 不能解决循环依赖的场景原型 prototype 作用域 Bean无法解决原型 Bean 不使用三级缓存构造器注入的循环依赖无法解决实例化阶段就需要依赖对象还没机会放入三级缓存AOP 代理 循环依赖三级缓存的 ObjectFactory 就是用来生成代理对象这个场景可以支持。总结Spring 只能解决单例、setter / 字段注入场景循环依赖构造注入、原型 Bean 循环依赖直接抛出异常。七、IOC 容器带来的工程价值解耦对象之间不再硬编码依赖更换实现类不需要修改业务代码面向接口编程。统一管理 Bean 生命周期创建、初始化、销毁由容器统一管控。天然支持 AOP 增强IOC 把 Bean 统一托管才有机会生成代理对象实现事务、切面等能力。单元测试友好依赖可以轻松 Mock不需要手动 new 一整条依赖链。集中配置管理配合注解、配置文件统一管理对象创建逻辑。八、实战开发踩坑总结单例 Bean 是容器全局唯一不要在 Bean 中定义可变成员变量会出现线程安全问题原型 Bean 不要存到全局静态变量会导致原型失效PostConstruct是初始化方法构造器是实例化不要混淆构造器执行时依赖还没注入完成循环依赖优先改成设计层面解耦不要单纯依赖三级缓存兜底业务上循环依赖本身就是设计问题。九、小测Q1IOC 和 DI 的区别AIOC 是控制反转的设计思想把对象控制权交给容器DI 依赖注入是 IOC 的实现手段容器自动装配对象依赖。Q2BeanFactory 和 ApplicationContext 区别ABeanFactory 是底层接口懒加载 BeanApplicationContext 是子接口预加载单例 Bean提供国际化、事件发布等扩展能力开发中常用。Q3Bean 生命周期顺序PostConstruct 执行时机A实例化 → 属性填充 → Aware 回调 → BeanPostProcessor 前置处理 → PostConstruct → InitializingBean → init-method → 后置处理器 → 放入单例池使用。Q4三级缓存分别作用为什么需要三级两级行不行A一级完整 Bean二级半成品 Bean三级对象工厂用来生成 AOP 代理对象。如果没有三级缓存循环依赖场景下 AOP 代理无法生成两级缓存只能处理普通无代理 Bean。Q5哪些场景循环依赖 Spring 无法解决A原型 Bean、构造器注入产生的循环依赖。结语IOC 是 Spring 体系最底层的地基AOP、声明式事务、SpringBoot 自动配置全部构建在 IOC 容器之上。很多开发停留在会用Autowired但是理解 Bean 创建流程、三级缓存原理之后遇到线上 Bean 创建异常、循环依赖报错、AOP 失效问题才能快速定位根因。 掌握 IOC 之后再去学习 AOP理解难度会大幅降低。

相关新闻

PMP三个月高效备考计划与实战技巧

PMP三个月高效备考计划与实战技巧

1. 项目概述:三个月PMP备考计划的价值与定位PMP认证作为项目管理领域的黄金标准,每年吸引着数十万职场人士投入备考。但面对厚达700多页的PMBOK指南和复杂的知识体系,多数考生都会陷入"从入门到放弃"的困境。这套三个月通关计划表正…

2026/10/16 17:08:11 阅读更多 →
微信小程序宠物寄养平台开发:数据库设计、笼位库存与订单状态机实战

微信小程序宠物寄养平台开发:数据库设计、笼位库存与订单状态机实战

简介:本资源为基于微信小程序的宠物寄养平台毕业设计论文,面向计算机相关专业学生及需要完成小程序类毕设的开发者,帮助解决选题、系统设计与论文撰写问题。压缩包内共1个doc文件,约1.18MB,内容为完整论文文档&#xf…

2026/10/15 13:33:03 阅读更多 →
Windows上同时安装MySQL 5.7和8.0:双实例共存完整指南

Windows上同时安装MySQL 5.7和8.0:双实例共存完整指南

1. 为什么非要在 Windows 上同时装两个 MySQL1.1 一个 3306 端口的痛,谁遇到谁知道看到这个标题的人,我猜十有八九都是同一个处境:手里维护的老项目数据库跑在 MySQL 5.7 上,新项目又明确要求 8.0,两边谁都不能随便卸载…

2026/10/15 13:33:03 阅读更多 →

最新新闻

2026年AI电商趋势全解析:内容生成、智能推荐与供应链落地

2026年AI电商趋势全解析:内容生成、智能推荐与供应链落地

1. 2026年AI电商会走到哪一步:先看三条硬逻辑每年这个时间点,圈子里都会有一波趋势预测。我入行电商技术这几年,见过太多概念从爆火到沉寂的周期,所以对“AI驱动的电商增长”这类话题,我习惯先把水分挤掉,看…

2026/10/16 19:26:16 阅读更多 →
线性代数怎么学?矩阵与向量空间的工程视角与numpy实操

线性代数怎么学?矩阵与向量空间的工程视角与numpy实操

直接开写吧。这篇关于线性代数的文章,我想了很久该怎么开头——因为"矩阵和向量空间"这几个字,对很多人来说就是大学里最抽象、最容易劝退的一章。教材里上来就是定义、定理、证明,你会算行列式,会做矩阵乘法&#xff0…

2026/10/16 19:26:16 阅读更多 →
基于PID神经元网络的多变量解耦控制与Simulink仿真

基于PID神经元网络的多变量解耦控制与Simulink仿真

简介:面向多变量系统控制场景,PID神经元网络解耦控制算法将经典PID的稳定调节与神经网络的自学习能力相结合,把相互耦合的MIMO系统分解为多个独立子系统,适合自动化、控制工程方向的学生与研究人员学习。压缩包共6个文件&#xff…

2026/10/16 19:25:15 阅读更多 →
GBDT回归树原理与手写实现:从梯度提升到工业级可解释建模

GBDT回归树原理与手写实现:从梯度提升到工业级可解释建模

1. 为什么今天还要深挖GBDT回归树——它不是“老古董”,而是工业级建模的压舱石你可能在面试里被问过“GBDT和XGBoost有什么区别”,也可能在Kaggle比赛里抄过几行lightgbm的fit代码,但当你面对一份销售预测数据、一份设备剩余寿命评估表、或者…

2026/10/16 19:25:15 阅读更多 →
LangGraph多智能体系统设计:状态图编排与工程实践全解析

LangGraph多智能体系统设计:状态图编排与工程实践全解析

多智能体系统这两年听起来特别火,但真上手做过的都知道,绝大多数问题根本不在模型选择上,而在于你拿什么把多个Agent组织起来。我最早用纯Python写调度循环,状态机全靠手工维护,改一个分支逻辑要牵出一堆全局变量&…

2026/10/16 19:25:15 阅读更多 →
OpenCV直方图四件套:图像增强与检索的统计基石

OpenCV直方图四件套:图像增强与检索的统计基石

1. 先从像素的"人口普查"说起直方图是图像处理里被严重低估的一类工具。很多人学习 OpenCV 时,注意力全被各种滤波、边缘检测、特征点吸引走了,结果做到图像增强、目标识别、图像检索的时候才发现,直方图这个基础能力才是绕不开的底…

2026/10/16 19:25:15 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →