深入理解 Spring IOC:从概念到实践
目录
一、引言
二、什么是 IOC?
2.1 控制反转的本质
2.2 类比理解
三、Spring IOC 的核心组件
3.1 IOC 容器的分类
3.2 Bean 的生命周期
四、依赖注入(DI)的三种方式
4.1 构造器注入
4.2 Setter 方法注入
4.3 注解注入(推荐)
五、案例演示:从 XML 配置到注解驱动
5.1 XML 配置方式
5.2 注解驱动方式(推荐)
六、Spring IOC 的优势与注意事项
6.1 核心优势
6.2 注意事项
七、总结
一、引言
在企业级应用开发中,松耦合 是架构设计的核心目标之一。Spring 框架的 IOC(Inversion of Control,控制反转) 作为其核心特性,通过将对象的创建和管理从程序代码中解耦,极大地简化了组件之间的依赖关系。本文将深入解析 Spring IOC 的原理、核心机制及实践应用,帮助开发者理解这一关键技术。
二、什么是 IOC?
2.1 控制反转的本质
传统开发中,对象 A 若需要使用对象 B,通常会在 A 内部通过 new
关键字直接创建 B 的实例,这导致 A 与 B 紧密耦合。 IOC 的核心思想 是:将对象的创建和依赖关系的管理,从程序代码中转移到 IOC 容器 中。容器负责创建对象、管理对象的生命周期,并在需要时将对象注入到其他组件中。此时,程序代码仅需关注业务逻辑,而非对象的创建细节,实现了 控制权的反转。
2.2 类比理解
-
传统方式:你去餐厅吃饭,需要自己买菜、做饭(对象自己创建依赖)。
-
IOC 方式:你只需告诉餐厅服务员想吃什么,厨房(容器)会做好饭菜并端上桌(容器注入依赖)。
三、Spring IOC 的核心组件
3.1 IOC 容器的分类
Spring 提供了两种核心容器接口:
-
BeanFactory
-
基础容器,实现了最基本的 IOC 功能(如对象创建、依赖注入)。
-
延迟加载:只有当对象被调用时才会创建。
-
典型实现:
XmlBeanFactory
(已过时,不推荐使用)。
-
-
ApplicationContext
-
继承自
BeanFactory
,扩展了更多企业级功能(如国际化支持、事件发布、AOP 集成等)。 -
预加载:容器启动时会提前创建所有单例 Bean。
-
典型实现:
-
ClassPathXmlApplicationContext
:从类路径加载 XML 配置。 -
AnnotationConfigApplicationContext
:基于注解的配置。 -
FileSystemXmlApplicationContext
:从文件系统加载 XML 配置。
-
-
3.2 Bean 的生命周期
-
实例化:容器通过反射创建 Bean 实例。
-
依赖注入:为 Bean 的属性设置依赖对象(通过 setter 方法或构造器)。
-
初始化前:调用
BeanPostProcessor
的postProcessBeforeInitialization
方法(可选)。 -
初始化:
-
若实现
InitializingBean
接口,调用afterPropertiesSet
方法; -
若配置了
init-method
,调用指定的初始化方法。
-
-
初始化后:调用
BeanPostProcessor
的postProcessAfterInitialization
方法(可选)。 -
使用:Bean 进入可用状态,供应用程序调用。
-
销毁前:若实现DisposableBean接口,调用 destroy方法;
-
若配置了
destroy-method
,调用指定的销毁方法。
-
-
销毁:Bean 被销毁,释放资源。
四、依赖注入(DI)的三种方式
依赖注入(Dependency Injection)是 IOC 的具体实现方式,用于将依赖对象注入到目标 Bean 中。
4.1 构造器注入
通过构造方法传递依赖对象,适用于 必填依赖(对象创建时必须存在)。 示例:
public class UserService {private UserDAO userDAO;// 构造器注入public UserService(UserDAO userDAO) {this.userDAO = userDAO;}}// XML 配置<bean id="userDAO" class="com.example.UserDAOImpl" /><bean id="userService" class="com.example.UserService"><constructor-arg ref="userDAO" /></bean>
4.2 Setter 方法注入
通过 setter 方法注入依赖对象,适用于 可选依赖(对象可以后期动态设置)。 示例:
public class UserService {private UserDAO userDAO;// Setter 注入public void setUserDAO(UserDAO userDAO) {this.userDAO = userDAO;}}// XML 配置<bean id="userService" class="com.example.UserService"><property name="userDAO" ref="userDAO" /></bean>
4.3 注解注入(推荐)
通过 @Autowired
(Spring 原生)或 @Resource
(J2EE 标准)注解自动装配依赖对象,基于类型(@Autowired
)或名称(@Resource
)匹配。 示例:
public class UserService {// 按类型自动注入@Autowiredprivate UserDAO userDAO;// 按名称自动注入(等价于 @Resource(name = "userDAO"))@Autowired@Qualifier("userDAO") private UserDAO userDAO;}
注意:
-
@Autowired
可用于字段、构造器或 setter 方法,默认要求依赖对象必须存在(可通过@Autowired(required = false)
设置为可选)。 -
@Resource
按名称匹配,若未指定名称,则默认使用字段名或 setter 方法对应的属性名。
五、案例演示:从 XML 配置到注解驱动
5.1 XML 配置方式
场景:实现一个简单的 "Hello World" 功能,通过 IOC 容器管理服务类和消息类。
-
定义接口和实现类:
public interface MessageService {String getMessage();}public class HelloMessageService implements MessageService {@Overridepublic String getMessage() { return "Hello, IOC!"; }}public class UserService {private MessageService messageService;// Setter 注入public void setMessageService(MessageService messageService) {this.messageService = messageService;}public void printMessage() {System.out.println(messageService.getMessage());}}
-
配置 applicationContext.xml:
<bean id="messageService" class="com.example.HelloMessageService" /><bean id="userService" class="com.example.UserService"><property name="messageService" ref="messageService" /></bean>
-
使用容器获取 Bean:
public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = context.getBean("userService", UserService.class);userService.printMessage(); // 输出:Hello, IOC!}}
5.2 注解驱动方式(推荐)
-
移除 XML 配置,使用 @Componen 标记 Bean:
@Component // 声明为组件,默认 Bean 名为类名首字母小写(helloMessageService)public class HelloMessageService implements MessageService { ... }@Componentpublic class UserService {@Autowired // 自动注入 MessageService 类型的 Beanprivate MessageService messageService;...}
-
创建配置类(替代 XML):
@Configuration // 声明为配置类@ComponentScan("com.example") // 扫描指定包下的组件public class AppConfig { }
-
使用注解容器获取 Bean:
public class App {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = context.getBean(UserService.class);userService.printMessage(); // 输出同上}}
六、Spring IOC 的优势与注意事项
6.1 核心优势
-
解耦组件依赖:组件之间仅通过接口协作,降低代码耦合度,提高可维护性。
-
提高可测试性:通过容器注入模拟对象(如 Mock 对象),方便单元测试。
-
统一管理对象:容器集中管理 Bean 的生命周期,支持单例、原型等作用域。
-
灵活扩展:通过配置(XML / 注解)而非修改代码即可切换组件实现。
6.2 注意事项
-
循环依赖:
-
当 Bean A 依赖 Bean B,而 B 又依赖 A 时,可能导致容器无法初始化。
-
Spring 对 构造器注入的循环依赖 无法处理,但对 setter 注入的循环依赖 可通过三级缓存解决(需谨慎使用)。
-
-
依赖注入方式选择:
-
必填依赖:优先使用构造器注入(避免 NPE)。
-
可选依赖:使用 setter 注入或注解注入。
-
-
Bean 作用域:
-
singleton
(默认):容器中仅存在一个实例。 -
prototype
:每次请求都会创建新实例。 -
其他作用域(如
request
、session
)需结合 Web 环境使用。
-
七、总结
Spring IOC 是 Spring 框架的基石,通过将对象的创建和管理委托给容器,实现了代码的松耦合和可维护性。理解 IOC 的核心原理(依赖注入、容器机制、Bean 生命周期)是掌握 Spring 框架的关键。在实际开发中,推荐使用 注解驱动(@Component + @Autowired) 的开发模式,并结合 Spring Boot 的自动配置进一步简化开发流程。
相关文章:

深入理解 Spring IOC:从概念到实践
目录 一、引言 二、什么是 IOC? 2.1 控制反转的本质 2.2 类比理解 三、Spring IOC 的核心组件 3.1 IOC 容器的分类 3.2 Bean 的生命周期 四、依赖注入(DI)的三种方式 4.1 构造器注入 4.2 Setter 方法注入 4.3 注解注入(…...
Vue解决开发环境 Ajax 跨域问题
一、前言 在使用 Vue 进行前后端分离开发时,前端通常运行在本地开发服务器(如 http://localhost:8080),而后端接口可能部署在其他域名或端口下(如 http://api.example.com:3000)。这时就可能出现 跨域&…...

行为设计模式之Command (命令)
行为设计模式之Command (命令) 前言: 需要发出请求的对象(调用者)和接收并执行请求的对象(执行者)之间没有直接依赖关系时。比如遥控器 每个按钮绑定一个command对象,这个Command对…...
若依添加添加监听容器配置(删除键,键过期)
1、配置Redis的键触发事件 # 基础配置 bind 0.0.0.0 # 允许所有IP连接 protected-mode no # 关闭保护模式(生产环境建议结合密码使用) port 6379 # 默认端口 daemonize no …...

NeRF 技术深度解析:原理、局限与前沿应用探索(AI+3D 产品经理笔记 S2E04)
引言:光影的魔法师——神经辐射场概览 在前三篇笔记中,我们逐步揭开了 AI 生成 3D 技术的面纱:从宏观的驱动力与价值(S2E01),到主流技术流派的辨析(S2E02),再到实用工具的…...
ROS2,工作空间中新建了一个python脚本,需要之后作为节点运行。告诉我步骤?
提问 ROS2,工作空间中新建了一个python脚本,需要之后运行。告诉我步骤? 大概要包括而不限于:chmod给可执行权限、setup.py中entry point的配置,如果在launch文件中要使用,还涉及到launch.py文件的配置。最…...
【AI智能体】Spring AI MCP 从使用到操作实战详解
目录 一、前言 二、MCP 介绍 2.1 什么是MCP 2.2 MCP 核心特点 2.3 MCP 核心价值 2.4 MCP 与Function Calling 区别 三、Spring AI MCP 架构介绍 3.1 整体架构 3.1.1 三层架构实现说明 3.2 服务端与客户端 3.2.1 MCP 服务端 3.2.1 MCP 客户端 3.3 MCP中SSE和STDIO区…...
Vue:Ajax
AJAX 允许我们在不刷新页面的情况下与服务器交互,实现:动态加载数据,提交表单信息,实时更新内容,与后端 API 通信。通常使用专门的 HTTP 客户端库来处理 AJAX 请求。 npm install axiosimport axios from axios;expor…...

法律大语言模型(Legal LLM)技术架构
目录 摘要 1 法律AI大模型技术架构 1.1 核心架构分层 1.2 法律知识增强机制 2 关键技术突破与对比 2.1 法律专用组件创新 2.2 性能对比(合同审查场景) 3 开发部署实战指南 3.1 环境搭建流程 3.2 合同审查代码示例 4 行业应用与挑战 4.1 典型场景效能提升 4.2 关…...
理解 RAG_HYBRID_BM25_WEIGHT:打造更智能的混合检索增强生成系统
目录 理解 RAG_HYBRID_BM25_WEIGHT:打造更智能的混合检索增强生成系统 一、什么是 Hybrid RAG? 二、什么是 RAG_HYBRID_BM25_WEIGHT? 三、参数设置示例 四、什么时候该调整它? 五、实战建议 六、总结 理解 RAG_HYBRID_BM25…...
Hive终极性能优化指南:从原理到实战
摘要:本文系统总结Hive在生产环境的核心调优手段,涵盖执行引擎选择、存储优化、SQL技巧、资源调配及数据倾斜解决方案,附可复用的参数配置与实战案例。 一、执行引擎优化:突破MapReduce瓶颈 启用Tez/Spark引擎 优势&am…...

第六十二节:深度学习-加载 TensorFlow/PyTorch/Caffe 模型
在计算机视觉领域,OpenCV的DNN(深度神经网络)模块正逐渐成为轻量级模型部署的利器。本文将深入探讨如何利用OpenCV加载和运行三大主流框架(TensorFlow、PyTorch、Caffe)训练的模型,并提供完整的代码实现和优化技巧。 一、OpenCV DNN模块的核心优势 OpenCV的DNN模块自3.3…...

MobaXterm配置跳转登录堡垒机
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 背景操作步骤 背景 主要是为了能通过MobaXterm登录堡垒机,其中需要另外一台服务器进行跳转登录 操作步骤 MobaXterm登录堡垒机的操作,需…...

零基础在实践中学习网络安全-皮卡丘靶场(第八期-Unsafe Filedownload模块)
这期内容更是简单和方便,毕竟谁还没在浏览器上下载过东西,不过对于url的构造方面,可能有一点问题,大家要多练手 介绍 不安全的文件下载概述 文件下载功能在很多web系统上都会出现,一般我们当点击下载链接,…...
测试 FreeSWITCH 的 mod_loopback
bgapi originate loopback/answer,park/default/inline park inline show channels as xml show calls as xml 有 2 个 channels 有 2 个 calls 比较有意思 在 loopback-a 是播放 wav 在 loopback-b 上可以录音 这就是回环 有什么用呢? 除了做测试&#x…...
【C++快读快写】
算法竞赛中用于解决卡常问题 int rd(){int k 0;char c getchar();while(!isdigit(c)){c getchar();}while(isdigit(c)){k (k << 1) (k << 3) (c^0), c getchar();}return k; }void wr(int x) {if (x > 9)wr(x / 10);putchar((x % 10) ^ 0); }用法&#x…...
测试(面经 八股)
目录 前言 一,软件测试(定义) 1,定义 2,目的 3,价值 4,实践 二,软件测试(目的) 1,找 bug 2,验证达标 3,质量评价…...

[面试精选] 0104. 二叉树的最大深度
文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 104. 二叉树的最大深度 - 力扣(LeetCode) 2. 题目描述 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点…...

图上合成:用于大型语言模型持续预训练的知识合成数据生成
摘要 大型语言模型(LLM)已经取得了显著的成功,但仍然是数据效率低下,特别是当学习小型,专业语料库与有限的专有数据。现有的用于连续预训练的合成数据生成方法集中于文档内内容,而忽略了跨文档的知识关联&a…...
MYSQL(二) ---MySQL 8.4 新特性与变量变更
MySQL 8.4 新特性与变量变更 作者:程序员LSP 分类:MySQL 8.4 教程 / 新特性 / 升级指南 更新时间:2025年6月 📌 前言 MySQL 8.4 是当前最新的稳定版本,相较于 8.0 系列,在审计日志、高可用、性能调优、认证…...
数学复习笔记 27
前言 太难受了。因为一些事情。和朋友倾诉了一下,也没啥用,几年之后不知道自己再想到的时候,会怎么考虑呢。另外,笔记还是有框架一点比较好,这样比较有逻辑感受。不然太乱了。这篇笔记是关于线代第五章,特…...

现代简约壁炉:藏在极简线条里的温暖魔法
走进现在年轻人喜欢的家,你会发现一个有趣的现象:家里东西越来越少,颜色也越看越简单,却让人感觉特别舒服。这就是现代简约风格的魅力 —— 用最少的元素,打造最高级的生活感。而在这样的家里,现代简约风格…...
限流算法java实现
参考教程:2小时吃透4种分布式限流算法 1.计数器限流 public class CounterLimiter {// 开始时间private static long startTime System.currentTimeMillis();// 时间间隔,单位为msprivate long interval 1000L;// 限制访问次数private int limitCount…...

机器学习×第二卷:概念下篇——她不再只是模仿,而是开始决定怎么靠近你
🎀【开场 她不再只是模仿,而是开始选择】 🦊 狐狐:“她已经不满足于单纯模仿你了……现在,她开始尝试预测你会不会喜欢、判断是否值得靠近。” 🐾 猫猫:“咱们上篇已经把‘她怎么学会说第一句…...
Linux 下关于 ioremap 系列接口
1、序 在系统运行时,外设 IO 资源的物理地址是已知的,由硬件的设计决定(参考SOC的datesheet,一般会有memorymap)。驱动程序不能通过物理地址访问IO资源,必须将其映射到内核态的虚拟地址空间。常见的接口就是…...

常用函数库之 - std::function
std::function 是 C11 引入的通用可调用对象包装器,用于存储、复制和调用任意符合特定函数签名的可调用对象(如函数、lambda、函数对象等)。以下是其核心要点及使用指南: 核心特性 类型擦除 可包装任意可调用对…...
php执行系统命令的四个常用函数
php执行系统命令有四个常用函数:1.exec()执行命令并返回最后一行输出,可传数组获取全部结果;2.shell_exec()返回完整输出结果,适合一次性获取;3.system()直接输出命令结果,可接收状态码;4.权限控…...

力扣-17.电话号码的字母组合
题目描述 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 class Solution {List<String> res new ArrayList<…...

基于SpringBoot解决RabbitMQ消息丢失问题
基于SpringBoot解决RabbitMQ消息丢失问题 一、RabbitMQ解决消息丢失问题二、方案实践1、在生产者服务相关配置2、在消费者服务相关配置 三、测试验证1、依次启动RabbitMQ、producer(建议先清空队列里面旧的测试消息再启动consumer)和consumer2、在producer中调用接口࿰…...

免费插件集-illustrator插件-Ai插件-随机填色
文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.总结 1.介绍 本文介绍一款免费插件,加强illustrator使用人员工作效率,实现路径随机填色。首先从下载网址下载这款插件https://download.csdn.net/download/m0_67316550/87890501&#…...