深入理解 Spring IOC:从概念到实践

news2025/6/8 21:39:26

目录

一、引言

二、什么是 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 提供了两种核心容器接口:

  1. BeanFactory

    • 基础容器,实现了最基本的 IOC 功能(如对象创建、依赖注入)。

    • 延迟加载:只有当对象被调用时才会创建。

    • 典型实现:XmlBeanFactory(已过时,不推荐使用)。

  2. ApplicationContext

    • 继承自 BeanFactory,扩展了更多企业级功能(如国际化支持、事件发布、AOP 集成等)。

    • 预加载:容器启动时会提前创建所有单例 Bean。

    • 典型实现:

      • ClassPathXmlApplicationContext:从类路径加载 XML 配置。

      • AnnotationConfigApplicationContext:基于注解的配置。

      • FileSystemXmlApplicationContext:从文件系统加载 XML 配置。

3.2 Bean 的生命周期

  1. 实例化:容器通过反射创建 Bean 实例。

  2. 依赖注入:为 Bean 的属性设置依赖对象(通过 setter 方法或构造器)。

  3. 初始化前:调用 BeanPostProcessorpostProcessBeforeInitialization 方法(可选)。

  4. 初始化:

    • 若实现 InitializingBean 接口,调用 afterPropertiesSet 方法;

    • 若配置了 init-method,调用指定的初始化方法。

  5. 初始化后:调用 BeanPostProcessorpostProcessAfterInitialization 方法(可选)。

  6. 使用:Bean 进入可用状态,供应用程序调用。

  7. 销毁前:若实现DisposableBean接口,调用 destroy方法;

    • 若配置了 destroy-method,调用指定的销毁方法。

  8. 销毁: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 {
     // 按类型自动注入
     @Autowired
     private 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 容器管理服务类和消息类。

  1. 定义接口和实现类:

     public interface MessageService {
         String getMessage();
     }
     ​
     public class HelloMessageService implements MessageService {
         @Override
         public 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());
         }
     }
  2. 配置 applicationContext.xml:

     <bean id="messageService" class="com.example.HelloMessageService" />
     <bean id="userService" class="com.example.UserService">
         <property name="messageService" ref="messageService" />
     </bean>
  3. 使用容器获取 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 注解驱动方式(推荐)

  1. 移除 XML 配置,使用 @Componen 标记 Bean:

     @Component // 声明为组件,默认 Bean 名为类名首字母小写(helloMessageService)
     public class HelloMessageService implements MessageService { ... }
     ​
     @Component
     public class UserService {
         @Autowired // 自动注入 MessageService 类型的 Bean
         private MessageService messageService;
         ...
     }

  2. 创建配置类(替代 XML):

     @Configuration // 声明为配置类
     @ComponentScan("com.example") // 扫描指定包下的组件
     public class AppConfig { }
  3. 使用注解容器获取 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 核心优势

  1. 解耦组件依赖:组件之间仅通过接口协作,降低代码耦合度,提高可维护性。

  2. 提高可测试性:通过容器注入模拟对象(如 Mock 对象),方便单元测试。

  3. 统一管理对象:容器集中管理 Bean 的生命周期,支持单例、原型等作用域。

  4. 灵活扩展:通过配置(XML / 注解)而非修改代码即可切换组件实现。

6.2 注意事项

  1. 循环依赖:

    • 当 Bean A 依赖 Bean B,而 B 又依赖 A 时,可能导致容器无法初始化。

    • Spring 对 构造器注入的循环依赖 无法处理,但对 setter 注入的循环依赖 可通过三级缓存解决(需谨慎使用)。

  2. 依赖注入方式选择:

    • 必填依赖:优先使用构造器注入(避免 NPE)。

    • 可选依赖:使用 setter 注入或注解注入。

  3. Bean 作用域:

    • singleton(默认):容器中仅存在一个实例。

    • prototype:每次请求都会创建新实例。

    • 其他作用域(如 requestsession)需结合 Web 环境使用。

七、总结

Spring IOC 是 Spring 框架的基石,通过将对象的创建和管理委托给容器,实现了代码的松耦合和可维护性。理解 IOC 的核心原理(依赖注入、容器机制、Bean 生命周期)是掌握 Spring 框架的关键。在实际开发中,推荐使用 注解驱动(@Component + @Autowired) 的开发模式,并结合 Spring Boot 的自动配置进一步简化开发流程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

行为设计模式之Command (命令)

行为设计模式之Command &#xff08;命令&#xff09; 前言&#xff1a; 需要发出请求的对象&#xff08;调用者&#xff09;和接收并执行请求的对象&#xff08;执行者&#xff09;之间没有直接依赖关系时。比如遥控器 每个按钮绑定一个command对象&#xff0c;这个Command对…

NeRF 技术深度解析:原理、局限与前沿应用探索(AI+3D 产品经理笔记 S2E04)

引言&#xff1a;光影的魔法师——神经辐射场概览 在前三篇笔记中&#xff0c;我们逐步揭开了 AI 生成 3D 技术的面纱&#xff1a;从宏观的驱动力与价值&#xff08;S2E01&#xff09;&#xff0c;到主流技术流派的辨析&#xff08;S2E02&#xff09;&#xff0c;再到实用工具的…

法律大语言模型(Legal LLM)技术架构

目录 摘要 1 法律AI大模型技术架构 1.1 核心架构分层 1.2 法律知识增强机制 2 关键技术突破与对比 2.1 法律专用组件创新 2.2 性能对比(合同审查场景) 3 开发部署实战指南 3.1 环境搭建流程 3.2 合同审查代码示例 4 行业应用与挑战 4.1 典型场景效能提升 4.2 关…

第六十二节:深度学习-加载 TensorFlow/PyTorch/Caffe 模型

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

MobaXterm配置跳转登录堡垒机

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

零基础在实践中学习网络安全-皮卡丘靶场(第八期-Unsafe Filedownload模块)

这期内容更是简单和方便&#xff0c;毕竟谁还没在浏览器上下载过东西&#xff0c;不过对于url的构造方面&#xff0c;可能有一点问题&#xff0c;大家要多练手 介绍 不安全的文件下载概述 文件下载功能在很多web系统上都会出现&#xff0c;一般我们当点击下载链接&#xff0c…

[面试精选] 0104. 二叉树的最大深度

文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 2. 题目描述 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点…

图上合成:用于大型语言模型持续预训练的知识合成数据生成

摘要 大型语言模型&#xff08;LLM&#xff09;已经取得了显著的成功&#xff0c;但仍然是数据效率低下&#xff0c;特别是当学习小型&#xff0c;专业语料库与有限的专有数据。现有的用于连续预训练的合成数据生成方法集中于文档内内容&#xff0c;而忽略了跨文档的知识关联&a…

现代简约壁炉:藏在极简线条里的温暖魔法

走进现在年轻人喜欢的家&#xff0c;你会发现一个有趣的现象&#xff1a;家里东西越来越少&#xff0c;颜色也越看越简单&#xff0c;却让人感觉特别舒服。这就是现代简约风格的魅力 —— 用最少的元素&#xff0c;打造最高级的生活感。而在这样的家里&#xff0c;现代简约风格…

机器学习×第二卷:概念下篇——她不再只是模仿,而是开始决定怎么靠近你

&#x1f380;【开场 她不再只是模仿&#xff0c;而是开始选择】 &#x1f98a; 狐狐&#xff1a;“她已经不满足于单纯模仿你了……现在&#xff0c;她开始尝试预测你会不会喜欢、判断是否值得靠近。” &#x1f43e; 猫猫&#xff1a;“咱们上篇已经把‘她怎么学会说第一句…

常用函数库之 - std::function

std::function 是 C11 引入的通用可调用对象包装器&#xff0c;用于存储、复制和调用任意符合特定函数签名的可调用对象&#xff08;如函数、lambda、函数对象等&#xff09;。以下是其核心要点及使用指南&#xff1a; ​​核心特性​​ ​​类型擦除​​ 可包装任意可调用对…

力扣-17.电话号码的字母组合

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

基于SpringBoot解决RabbitMQ消息丢失问题

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

免费插件集-illustrator插件-Ai插件-随机填色

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

Web设计之登录网页源码分享,PHP数据库连接,可一键运行!

HTML 页面结构&#xff08;index.html&#xff09; 1. 流星雨动态背景 2. 主体界面&#xff08;包含登录和注册表单&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport&qu…

Cursor + Claude 4:微信小程序流量主变现开发实战案例

前言 随着微信小程序生态的日益成熟&#xff0c;越来越多的开发者开始关注如何通过小程序实现流量变现。本文将详细介绍如何使用Cursor编辑器结合Claude 4 AI助手&#xff0c;快速开发一个具备流量主变现功能的微信小程序&#xff0c;并分享实际的开发经验和变现策略。 项目…

Redis Key过期策略

概述 Redis的Key过期策略是其内存管理系统的核心组成部分&#xff0c;主要包括「被动过期」、「主动过期」和「内存淘汰」三个机制。其中「内存淘汰」相关内容已经在上一篇「Redis内存淘汰策略」中进行了详细的讲解&#xff0c;有信兴趣的同学可以在回顾上一篇文章。本文将着重…

【C/C++】实现固定地址函数调用

在 C 里&#xff0c;函数地址在程序运行期间通常是固定的&#xff0c;不过在动态链接库&#xff08;DLL&#xff09;或者共享库&#xff08;SO&#xff09;中&#xff0c;函数地址可能会因为地址空间布局随机化&#xff08;ASLR&#xff09;而改变。所以我们想要通过地址直接调…

多模态大语言模型arxiv论文略读(109)

Math-PUMA: Progressive Upward Multimodal Alignment to Enhance Mathematical Reasoning ➡️ 论文标题&#xff1a;Math-PUMA: Progressive Upward Multimodal Alignment to Enhance Mathematical Reasoning ➡️ 论文作者&#xff1a;Wenwen Zhuang, Xin Huang, Xiantao Z…

性能优化笔记

性能优化转载 https://www.cnblogs.com/tengzijian/p/17858112.html 性能优化的一般策略及方法 简言之&#xff0c;非必要&#xff0c;不优化。先保证良好的设计&#xff0c;编写易于理解和修改的整洁代码。如果现有的代码很糟糕&#xff0c;先清理重构&#xff0c;然后再考…