Spring IoC (Inversion of Control) 控制反转是什么?

news2025/5/11 21:50:51

我们分析一下 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");
    }
}

分析传统方式的问题:

  1. 高耦合: OrderService 直接依赖于 ProductServiceImplUserServiceImpl 这两个具体的实现类。如果将来想换成 MockProductServiceImpl 进行测试,或者换成 AdvancedProductServiceImpl,就需要修改 OrderService 的代码。
  2. 难以测试: 由于 OrderService 内部自己创建了依赖,很难在单元测试中替换掉 ProductServiceUserService 的真实实现为 Mock 对象。
  3. 资源管理复杂: 如果 ProductServiceUserService 需要复杂的初始化或资源管理(如数据库连接),OrderService 也需要关心这些,增加了内部管理职责。

现在来看看“有 IoC”的情况(控制反转):

在 IoC 模式下,OrderService 不再自己创建 ProductServiceUserService。它只声明它需要这些依赖,然后由一个外部的 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. 使用 OrderService
        orderService.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 的核心好处:

  1. 解耦 (Decoupling):
    • 组件不再依赖于具体的实现,而是依赖于抽象(接口)。
    • IoC 容器负责将具体的实现注入进来,使得更换实现变得容易,无需修改依赖方的代码。
  2. 易于测试 (Testability):
    • 在单元测试中,可以轻松地向被测对象注入 Mock 对象或测试桩 (Stub),从而隔离被测单元。
  3. 可维护性和可重用性 (Maintainability & Reusability):
    • 松耦合的组件更容易被理解、修改和重用。
  4. 集中管理 (Centralized Management):
    • 对象的创建和配置(比如数据库连接池的参数、事务管理器的配置等)都由 IoC 容器集中管理,使得配置更清晰,修改更方便。
  5. 面向接口编程的强化:
    • IoC 鼓励开发者面向接口编程,而不是面向实现编程,这本身就是一种良好的设计实践。

IoC 的实现方式:

最常见和重要的实现 IoC 的方式是依赖注入 (Dependency Injection, DI)。DI 是 IoC 原则的一种具体实现模式。Spring 框架就是通过 DI 来实现其 IoC 容器的。DI 主要有以下几种形式:

  1. 构造器注入 (Constructor Injection): 通过类的构造函数传递依赖。
  2. Setter 方法注入 (Setter Injection): 通过类的 setter 方法传递依赖。
  3. 接口注入 (Interface Injection): 实现一个特定接口,该接口包含一个注入依赖的方法(较少使用)。
  4. (Spring 特有) 字段注入 (Field Injection): 直接在字段上使用注解(如 @Autowired)注入依赖(简洁但不推荐用于业务逻辑,因其破坏封装性且不利于测试)。

总结:

IoC 是一种让你从繁琐的对象创建和依赖管理中解放出来的设计思想。你只需要告诉框架(IoC 容器)你需要什么,框架就会在合适的时候把你需要的东西准备好并交给你。这就像你以前自己做饭(控制一切),现在你只需要去餐厅点菜(声明需求),餐厅的厨师和服务员(IoC 容器)会把菜做好并端到你面前(注入依赖)。

Spring 框架的核心就是其强大的 IoC 容器,它负责创建和管理应用中的所有 Bean (被 Spring 管理的对象),并通过依赖注入来解决它们之间的依赖关系。

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

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

相关文章

VirtualBox 创建虚拟机并安装 Ubuntu 系统详细指南

VirtualBox 创建虚拟机并安装 Ubuntu 系统详细指南 一、准备工作1. 下载 Ubuntu 镜像2. 安装 VirtualBox二、创建虚拟机1. 新建虚拟机2. 分配内存3. 创建虚拟硬盘三、配置虚拟机1. 加载 Ubuntu 镜像2. 调整处理器核心数(可选)3. 启用 3D 加速(图形优化)四、安装 Ubuntu 系统…

触想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 …

【Linux笔记】系统的延迟任务、定时任务极其相关命令(at、crontab极其黑白名单等)

一、延时任务 1、概念 延时任务(Delayed Jobs)通常指在指定时间或特定条件满足后执行的任务。常见的实现方式包括 at 和 batch 命令,以及结合 cron 的调度功能。 2、命令 延时任务的命令最常用的是at命令,第二大节会详细介绍。…

使用阿里AI的API接口实现图片内容提取功能

参考链接地址:如何使用Qwen-VL模型_大模型服务平台百炼(Model Studio)-阿里云帮助中心 在windows下,使用python语言测试,版本:Python 3.8.9 一. 使用QVQ模型解决图片数学难题 import os import base64 import requests# base 64 …

从零开始搭建你的个人博客:使用 GitHub Pages 免费部署静态网站

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

C#串口通信

在C#中使用串口通信比较方便,.Net 提供了现成的类, SerialPort类。 本文不对原理啥的进行介绍,只介绍SerialPort类的使用。 SerialProt类内部是调用了CreateFile,WriteFile等WinAPI函数来实现串口通信。 在后期的Windows编程系…

服务器配置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中断循环 • 性能最优,…

redis----通用命令

文章目录 前言一、运行redis二、help [command]三、通用命令 前言 提示:这里可以添加本文要记录的大概内容: 学习一些通用命令 以下操作在windows中演示 提示:以下是本篇文章正文内容,下面案例可供参考 一、运行redis 我们先c…

IntelliJ IDEA 保姆级使用教程

文章目录 一、创建项目二、创建模块三、创建包四、创建类五、编写代码六、运行代码注意 七、IDEA 常见设置1、主题2、字体3、背景色 八、IDEA 常用快捷键九、IDEA 常见操作9.1、类操作9.1.1、删除类文件9.1.2、修改类名称注意 9.2、模块操作9.2.1、修改模块名快速查看 9.2.2、导…

Comfyui 与 SDwebui

ComfyUI和SD WebUI是基于Stable Diffusion模型的两种不同用户界面工具,它们在功能、用户体验和适用场景上各有优劣。 1. 功能与灵活性 ComfyUI:ComfyUI以其节点式工作流设计为核心,强调用户自定义和灵活性。用户可以通过连接不同的模块&…

WiseAD:基于视觉-语言模型的知识增强型端到端自动驾驶——论文阅读

《WiseAD: Knowledge Augmented End-to-End Autonomous Driving with Vision-Language Model》2024年12月发表,来自新加坡国立和浙大的论文。 在快速发展的视觉语言模型(VLM)中,一般人类知识和令人印象深刻的逻辑推理能力的出现&a…

探索SQLMesh中的Jinja宏:提升SQL查询的灵活性与复用性

在数据工程和数据分析领域,SQL是不可或缺的工具。随着项目复杂度的增加,如何高效地管理和复用SQL代码成为了一个重要课题。SQLMesh作为一款强大的工具,不仅支持标准的SQL语法,还引入了Jinja模板引擎的宏功能,极大地提升…

对Redis组件的深入探讨

目录 1、磁盘和内存 1.1、概念 1.2、区别 1.3、联系 2、redis基本特性 2.1、数据结构 2.2、性能 2.3、事件驱动架构 2.4、原子性 3、redis模型 3.1、单线程 3.2、事件驱动模型 3.3、epoll多路复用 4、数据持久化 4.1、RDB快照 4.2、AOF(Append Only…