Spring IoC (Inversion of Control) 控制反转是什么?
我们分析一下 IoC (Inversion of Control) 控制反转的核心思想。
核心思想:
IoC 是一种设计原则(Design Principle),它描述了一种软件设计模式,其中组件(对象)的创建、依赖关系的管理和生命周期的控制权从程序代码本身转移到了外部容器或框架。
简单来说,就是:你(你的代码/对象)不再自己主动去创建或查找你所依赖的对象,而是由一个外部的“老板”(IoC 容器)来创建并把你需要的对象“递”给你。
为了更好的理解,我们先看看“没有 IoC”的情况(传统的控制方式):
假设你有一个 OrderService
类,它需要一个 ProductService
来获取产品信息,还需要一个 UserService
来获取用户信息。
// 传统方式
public class OrderService {// OrderService 主动创建它依赖的对象private ProductService productService = new ProductServiceImpl();private UserService userService = new UserServiceImpl();public void placeOrder(String productId, String userId) {Product product = productService.getProductById(productId);User user = userService.getUserById(userId);// ... 创建订单逻辑 ...System.out.println("Order placed for product: " + product.getName() + " by user: " + user.getName());}
}// 依赖的接口和实现
interface ProductService { Product getProductById(String id); }
class ProductServiceImpl implements ProductService {public Product getProductById(String id) { /* ... */ return new Product(id, "Awesome Gadget"); }
}
interface UserService { User getUserById(String id); }
class UserServiceImpl implements UserService {public User getUserById(String id) { /* ... */ return new User(id, "John Doe"); }
}
class Product { String id, name; public Product(String id, String name) { this.id = id; this.name = name; } public String getName() { return name; } }
class User { String id, name; public User(String id, String name) { this.id = id; this.name = name; } public String getName() { return name; } }// 使用
public class MainApp {public static void main(String[] args) {OrderService orderService = new OrderService(); // OrderService自己搞定一切orderService.placeOrder("P123", "U456");}
}
分析传统方式的问题:
- 高耦合:
OrderService
直接依赖于ProductServiceImpl
和UserServiceImpl
这两个具体的实现类。如果将来想换成MockProductServiceImpl
进行测试,或者换成AdvancedProductServiceImpl
,就需要修改OrderService
的代码。 - 难以测试: 由于
OrderService
内部自己创建了依赖,很难在单元测试中替换掉ProductService
和UserService
的真实实现为 Mock 对象。 - 资源管理复杂: 如果
ProductService
或UserService
需要复杂的初始化或资源管理(如数据库连接),OrderService
也需要关心这些,增加了内部管理职责。
现在来看看“有 IoC”的情况(控制反转):
在 IoC 模式下,OrderService
不再自己创建 ProductService
和 UserService
。它只声明它需要这些依赖,然后由一个外部的 IoC 容器(比如 Spring 的 ApplicationContext
)来负责创建这些依赖的实例,并将它们“注入”到 OrderService
中。
// IoC 方式 (这里用构造器注入作为例子)
public class OrderService {// OrderService 只声明依赖,不负责创建private final ProductService productService;private final UserService userService;// 依赖通过构造器传入 (被IoC容器注入)public OrderService(ProductService productService, UserService userService) {this.productService = productService;this.userService = userService;}public void placeOrder(String productId, String userId) {Product product = productService.getProductById(productId);User user = userService.getUserById(userId);// ... 创建订单逻辑 ...System.out.println("Order placed for product: " + product.getName() + " by user: " + user.getName());}
}// 依赖的接口和实现 (与上面相同)
// ...// 模拟 IoC 容器的行为 (实际中这是Spring等框架做的)
public class IoCContainer {public static void main(String[] args) {// 1. 容器创建依赖对象ProductService productService = new ProductServiceImpl();UserService userService = new UserServiceImpl();// 2. 容器创建 OrderService,并将依赖注入OrderService orderService = new OrderService(productService, userService);// 3. 使用 OrderServiceorderService.placeOrder("P123", "U456");// 如果想用 Mock 对象测试ProductService mockProductService = new MockProductServiceImpl(); // 假设有这个测试类UserService mockUserService = new MockUserServiceImpl(); // 假设有这个测试类OrderService testOrderService = new OrderService(mockProductService, mockUserService);testOrderService.placeOrder("TestP", "TestU");}
}
// 假设Mock实现
class MockProductServiceImpl implements ProductService { /* ... */ public Product getProductById(String id) { return new Product(id, "Mock Product"); } }
class MockUserServiceImpl implements UserService { /* ... */ public User getUserById(String id) { return new User(id, "Mock User"); } }
“控制”指的是什么?“反转”又是什么意思?
-
控制 (Control): 指的是对对象创建和对象之间依赖关系管理的控制权。
- 对象创建的控制权: 由谁来
new
一个对象? - 依赖关系管理的控制权: 一个对象如何获取它所依赖的其他对象?
- 对象创建的控制权: 由谁来
-
反转 (Inversion):
- 传统方式: 应用程序代码(比如
OrderService
)掌握着控制权,它主动去创建或获取依赖。 - IoC 方式: 控制权被“反转”了。应用程序代码不再主动控制,而是将控制权交给了外部的 IoC 容器。对象是被动的接收它所需要的依赖。
- 传统方式: 应用程序代码(比如
IoC 的核心好处:
- 解耦 (Decoupling):
- 组件不再依赖于具体的实现,而是依赖于抽象(接口)。
- IoC 容器负责将具体的实现注入进来,使得更换实现变得容易,无需修改依赖方的代码。
- 易于测试 (Testability):
- 在单元测试中,可以轻松地向被测对象注入 Mock 对象或测试桩 (Stub),从而隔离被测单元。
- 可维护性和可重用性 (Maintainability & Reusability):
- 松耦合的组件更容易被理解、修改和重用。
- 集中管理 (Centralized Management):
- 对象的创建和配置(比如数据库连接池的参数、事务管理器的配置等)都由 IoC 容器集中管理,使得配置更清晰,修改更方便。
- 面向接口编程的强化:
- IoC 鼓励开发者面向接口编程,而不是面向实现编程,这本身就是一种良好的设计实践。
IoC 的实现方式:
最常见和重要的实现 IoC 的方式是依赖注入 (Dependency Injection, DI)。DI 是 IoC 原则的一种具体实现模式。Spring 框架就是通过 DI 来实现其 IoC 容器的。DI 主要有以下几种形式:
- 构造器注入 (Constructor Injection): 通过类的构造函数传递依赖。
- Setter 方法注入 (Setter Injection): 通过类的 setter 方法传递依赖。
- 接口注入 (Interface Injection): 实现一个特定接口,该接口包含一个注入依赖的方法(较少使用)。
- (Spring 特有) 字段注入 (Field Injection): 直接在字段上使用注解(如
@Autowired
)注入依赖(简洁但不推荐用于业务逻辑,因其破坏封装性且不利于测试)。
总结:
IoC 是一种让你从繁琐的对象创建和依赖管理中解放出来的设计思想。你只需要告诉框架(IoC 容器)你需要什么,框架就会在合适的时候把你需要的东西准备好并交给你。这就像你以前自己做饭(控制一切),现在你只需要去餐厅点菜(声明需求),餐厅的厨师和服务员(IoC 容器)会把菜做好并端到你面前(注入依赖)。
Spring 框架的核心就是其强大的 IoC 容器,它负责创建和管理应用中的所有 Bean (被 Spring 管理的对象),并通过依赖注入来解决它们之间的依赖关系。
相关文章:
Spring IoC (Inversion of Control) 控制反转是什么?
我们分析一下 IoC (Inversion of Control) 控制反转的核心思想。 核心思想: IoC 是一种设计原则(Design Principle),它描述了一种软件设计模式,其中组件(对象)的创建、依赖关系的管理和生命周…...
互联网大厂Java求职面试:核心技术点深度解析
互联网大厂Java求职面试:核心技术点深度解析 在互联网大厂的Java岗位面试中,技术总监级别的面试官通常会从实际业务场景出发,层层深入地考察候选人的技术能力。本文通过一个严肃专业的技术总监与搞笑但有技术潜力的程序员郑薪苦之间的互动对…...

VirtualBox 创建虚拟机并安装 Ubuntu 系统详细指南
VirtualBox 创建虚拟机并安装 Ubuntu 系统详细指南 一、准备工作1. 下载 Ubuntu 镜像2. 安装 VirtualBox二、创建虚拟机1. 新建虚拟机2. 分配内存3. 创建虚拟硬盘三、配置虚拟机1. 加载 Ubuntu 镜像2. 调整处理器核心数(可选)3. 启用 3D 加速(图形优化)四、安装 Ubuntu 系统…...
【算法基础】递归算法 - JAVA
一、递归基础 1.1 什么是递归算法 递归算法是一种通过函数调用自身来解决问题的方法。简单来说,就是"自己调用自己"。递归将复杂问题分解为同类的更简单子问题,直到达到易于直接解决的基本情况。 1.2 递归的核心要素 递归算法由两个关键部…...

触想CX-3588工控主板应用于移动AI数字人,赋能新型智能交互
一、行业发展背景 随着AI智能、自主导航和透明屏显示等技术的不断进步,以及用户对“拟人化”、“沉浸式”交互体验的期待,一种新型交互终端——“移动AI数字人”正在加速实现规模化商用。 各大展厅展馆、零售导购、教学政务甚至家庭场景中,移…...

【深入浅出MySQL】之数据类型介绍
【深入浅出MySQL】之数据类型介绍 MySQL中常见的数据类型一览为什么需要如此多的数据类型数值类型BIT(M)类型INT类型TINYINT类型BIGINT类型浮点数类型float类型DECIMAL(M,D)类型区别总结 字符串类型CHAR类型VARCHAR(M)类型 日期和时间类型enum和set类型 …...

Vue3响应式:effect作用域
# Vue3响应式: effect作用域 什么是Vue3响应式? 是一款流行的JavaScript框架,它提供了响应式和组件化的视图组织方式。在Vue3中,响应式是一种让数据变化自动反映在视图上的机制。当数据发生变化时,与之相关的视图会自动更新。 作用…...

25.5.4数据结构|哈夫曼树 学习笔记
知识点前言 一、搞清楚概念 ●权:___________ ●带权路径长度:__________ WPL所有的叶子结点的权值*路径长度之和 ●前缀编码:____________ 二、构造哈夫曼树 n个带权值的结点,构造哈夫曼树算法: 1、转化成n棵树组成的…...

RabbitMQ 深度解析:从核心组件到复杂应用场景
一.RabbitMQ简单介绍 消息队列作为分布式系统中不可或缺的组件,承担着解耦系统组件、保障数据可靠传输、提高系统吞吐量等重要职责。在众多消息队列产品中,RabbitMQ 凭借其可靠性和丰富的特性,在企业级应用中获得了广泛应用。 二.RabbitMQ …...
Python高级技巧及案例分析:提升编程能力的实践指南
目录 Python高级技巧及案例分析:提升编程能力的实践指南1. Python高级特性概述2. 函数式编程技巧2.1 高阶函数2.2 函数柯里化2.3 不可变数据结构3. 元编程与反射3.1 动态属性访问3.2 类装饰器3.3 元类应用4. 并发与异步编程4.1 多线程与线程池4.2 协程与asyncio4.3 多进程处理…...

【Linux笔记】系统的延迟任务、定时任务极其相关命令(at、crontab极其黑白名单等)
一、延时任务 1、概念 延时任务(Delayed Jobs)通常指在指定时间或特定条件满足后执行的任务。常见的实现方式包括 at 和 batch 命令,以及结合 cron 的调度功能。 2、命令 延时任务的命令最常用的是at命令,第二大节会详细介绍。…...
转换算子和行动算子的区别
转换算子和行动算子主要是在分布式计算框架(如 Apache Spark)里常用的概念,它们在功能、执行机制、返回结果等方面存在明显区别,以下为你详细介绍: 定义与功能 返回结果 如何在使用转换算子和行动算子时避免出现内存溢…...

使用阿里AI的API接口实现图片内容提取功能
参考链接地址:如何使用Qwen-VL模型_大模型服务平台百炼(Model Studio)-阿里云帮助中心 在windows下,使用python语言测试,版本:Python 3.8.9 一. 使用QVQ模型解决图片数学难题 import os import base64 import requests# base 64 …...
告别散乱的 @ExceptionHandler:实现统一、可维护的 Spring Boot 错误处理
Spring Boot 的异常处理机制一直都烂得可以。即便到了 2025 年,有了这么多进步和新版本,开发者们发现自己还是在跟 ControllerAdvice、分散各处的 ExceptionHandler 方法以及五花八门的响应结构较劲。这真的是一团糟。 无论你是在构建 REST API、微服务…...
MariaDB 与 MySQL 的关系:从同源到分道扬镳
MariaDB 与 MySQL 的关系:从同源到分道扬镳 1. 起源:MySQL 的辉煌与危机 MySQL 是最流行的开源关系型数据库之一,由瑞典公司 MySQL AB 开发,并于 1995 年 首次发布。由于其高性能、易用性和开源特性,MySQL 迅速成为 L…...

从零开始搭建你的个人博客:使用 GitHub Pages 免费部署静态网站
🌐 从零开始搭建你的个人博客:使用 GitHub Pages 免费部署静态网站 在互联网时代,拥有一个属于自己的网站不仅是一种展示方式,更是一种技术能力的体现。今天我们将一步步学习如何通过 GitHub Pages 搭建一个免费的个人博客或简历…...

C#串口通信
在C#中使用串口通信比较方便,.Net 提供了现成的类, SerialPort类。 本文不对原理啥的进行介绍,只介绍SerialPort类的使用。 SerialProt类内部是调用了CreateFile,WriteFile等WinAPI函数来实现串口通信。 在后期的Windows编程系…...
Qt 显示QRegExp 和 QtXml 不存在问题
QRegExp 和 QtXml 问题 在Qt6 中 已被弃用; 1)QRegExp 已被弃用,改用 QRegularExpression Qt5 → Qt6 重大变更:QRegExp 被移到了 Qt5Compat 模块,默认不在 Qt6 核心模块中。 错误类型解决方法QRegExp 找不到改用 Q…...
【训练】Qwen2.5VL 多机多卡 Grounding Box定位
之前的相关文章: 【深度学习】LLaMA-Factory微调sft Qwen2-VL进行印章识别 https://www.dong-blog.fun/post/1661 使用LLaMA-Factory微调sft Qwen2-VL-7B-Instruct https://www.dong-blog.fun/post/1762 构建最新的LLaMA-Factory镜像 https://www.dong-blog.f…...

服务器配置llama-factory问题解决
在配置运行llama-factory,环境问题后显示环境问题。这边给大家附上连接,我们的是liunx环境但是还是一样的。大家也记得先配置虚拟环境。 LLaMA-Factory部署以及微调大模型_llamafactory微调大模型-CSDN博客 之后大家看看遇到的问题是不是我这样。 AI搜索…...

Spring Boot + Vue 实现在线视频教育平台
一、项目技术选型 前端技术: HTML CSS JavaScript Vue.js 前端框架 后端技术: Spring Boot 轻量级后端框架 MyBatis 持久层框架 数据库: MySQL 5.x / 8.0 开发环境: IDE:Eclipse / IntelliJ IDEA JDK&…...

使用Jmeter进行核心API压力测试
最近公司有发布会,需要对全链路比较核心的API的进行压测,今天正好分享下压测软件Jmeter的使用。 一、什么是Jmeter? JMeter 是 Apache 旗下的基于 Java 的开源性能测试工具。最初被设计用于 Web 应用测试,现已扩展到可测试多种不同的应用程…...

JavaScript中数组和对象不同遍历方法的顺序规则
在JavaScript中,不同遍历方法的顺序规则和适用场景存在显著差异。以下是主要方法的遍历顺序总结: 一、数组遍历方法 for循环 • 严格按数组索引顺序遍历(0 → length-1) • 支持break和continue中断循环 • 性能最优,…...
【机器学习-线性回归-5】多元线性回归:概念、原理与实现详解
线性回归是机器学习中最基础且广泛应用的算法之一,而多元线性回归则是其重要扩展。本文将全面介绍多元线性回归的核心概念、数学原理及多种实现方式,帮助读者深入理解这一强大的预测工具。 1. 多元线性回归概述 1.1 什么是多元线性回归 多元线性回归(…...
【软件设计师:数据结构】1.数据结构基础(一)
一 线性表 1.线性表定义 线性表是n个元素的有限序列,通常记为(a1,a2,…,an)。 特点: 存在惟一的表头和表尾。除了表头外,表中的每一个元素均只有惟一的直接前驱。除了表尾外,表中的每一个元素均只有惟一的直接后继。2.线性表的存储结构 (1)顺序存储 是用一组地址连续…...
简单面试提问
Nosql非关系型数据库: Mongodb:开源、json形式储存、c编写 Redis:key-value形式储存,储存在内存,c编写 关系型数据库: sqlite;:轻量型、0配置、磁盘存储、支持多种语言 mysql:开源…...
探秘数据中台:五大核心平台的功能全景解析
数据中台作为企业数据资产的 “智慧中枢”,通过整合数据处理全流程的核心功能,实现数据价值的深度挖掘与高效应用。以下从五大核心平台出发,全面拆解数据中台的功能架构与应用价值。 一、数据可视化平台:让数据 “开口说话” 1.…...
leetcode 3342. 到达最后一个房间的最少时间 II 中等
有一个地窖,地窖中有 n x m 个房间,它们呈网格状排布。 给你一个大小为 n x m 的二维数组 moveTime ,其中 moveTime[i][j] 表示在这个时刻 以后 你才可以 开始 往这个房间 移动 。你在时刻 t 0 时从房间 (0, 0) 出发,每次可以移…...

redis----通用命令
文章目录 前言一、运行redis二、help [command]三、通用命令 前言 提示:这里可以添加本文要记录的大概内容: 学习一些通用命令 以下操作在windows中演示 提示:以下是本篇文章正文内容,下面案例可供参考 一、运行redis 我们先c…...
PostgreSQL 查看索引碎片的方法
PostgreSQL 查看索引碎片的方法 在 PostgreSQL 中,索引碎片(Index Fragmentation)是指索引由于频繁的插入、更新和删除操作导致物理存储不连续,从而影响查询性能的情况。以下是几种查看索引碎片的方法: 一 使用 pgstattuple 扩展 1.1 安装…...