当前位置: 首页 > news >正文

【.Net技术栈梳理】08-控制反转(IoC)与依赖注入(DI)

文章目录

  • 1. 控制反转 (IoC)
    • 1.1 概念
    • 1.2 好处
    • 1.3 IoC 容器
  • 2. 依赖注入 (DI)
    • 2.1 概念
    • 2.2 DI 的三种注入方式
    • 2.3 DI 的核心组件
    • 2.4 依赖的生命周期
    • 2.5 DI 在 ASP.NET Core 中的具体实现
    • 2.6 DI 工作流程

IoC 和 DI 是为了解决 OOP 中对象间耦合问题而生的设计原则和模式。

1. 控制反转 (IoC)

1.1 概念

IoC是一种设计原则,其核心是将程序的控制权反转。

  • 传统控制流程:在传统代码中,一个对象需要依赖另一个对象时,它会自己主动去 new 这个依赖(例如 var service = new EmailService();)。它控制着依赖的创建和生命周期。
public class OrderService
{private readonly EmailService _emailService;public OrderService(){// OrderService 控制着 EmailService 的创建和生命周期_emailService = new EmailService(); // "主动"获取依赖}public void ProcessOrder(){// ... 处理订单逻辑_emailService.Send(); // 使用依赖}
}
  • 反转后的控制流程:对象的依赖不再由自己创建,而是由一个外部容器(IoC 容器)来创建并“注入”给它。控制权从程序内部转移到了外部容器。
public class OrderService
{private readonly IEmailService _emailService;// 依赖通过构造函数"注入"进来public OrderService(IEmailService emailService) // 依赖抽象{// OrderService 失去了对依赖的控制权,只是被动接收_emailService = emailService;}public void ProcessOrder(){// ... 处理订单逻辑_emailService.Send(); // 使用依赖}
}
  • 比喻:传统方式就像你自己去厨房做菜(主动控制)。IoC 就像去餐厅点菜,你只需说“我要一份牛排”(声明需求),厨师(容器)会做好并端给你(注入依赖),你失去了做菜的控制权,但获得了更大的灵活性。

1.2 好处

  1. 解耦:OrderService 不再依赖具体的 EmailService,只依赖接口 IEmailService。

  2. 可测试性:在单元测试中,你可以轻松注入一个“模拟”的 IEmailService(使用 Moq 等框架)。

  3. 可扩展性:更换实现(如从 EmailService 换成 SendGridEmailService)只需修改配置,无需修改 OrderService 的代码。

1.3 IoC 容器

  • 概念:一个专门负责依赖创建和依赖注入的框架组件。它是 IoC 原则的物理实现。

  • 工作流程:

    1. 注册:告诉容器,当请求某个接口(抽象)时,应该创建哪个具体的实现类,并如何管理其生命周期(单例、每次请求创建新实例等)。

    2. 解析:在应用程序的入口点或需要的地方,请求容器创建一个对象(如 OrderService)。容器会分析其构造函数,发现它需要 IEmailService,于是自动创建 IEmailService 的具体实例,并注入到 OrderService 中,最后将完全构建好的 OrderService 返回。

2. 依赖注入 (DI)

2.1 概念

实现控制反转的一种具体技术模式。通过“注入”的方式(通过构造函数、属性或方法参数等方式)来实现控制权的反转,将一个对象的依赖项从外部传递给它。

  • 原理
    基于控制反转(IoC) 和依赖倒置原则(DIP),将依赖的创建权从使用方剥离,交给外部容器管理,从而实现解耦。

  • 目的:解耦。让类不再直接创建它的依赖,而是接收它们。这使得代码更易于测试(例如,在测试时可以轻松注入一个“模拟”依赖)和维护。

2.2 DI 的三种注入方式

// 1. 构造函数注入 (最常用、最推荐)
public class OrderService
{private readonly IEmailService _emailService;// 依赖通过构造函数传入public OrderService(IEmailService emailService){_emailService = emailService;}public void ProcessOrder(){// ... 业务逻辑_emailService.SendConfirmation();}
}// 2. 属性注入
public class OrderService
{// 依赖通过公共属性设置public IEmailService EmailService { get; set; }
}// 3. 方法注入
public class OrderService
{// 依赖通过方法参数传入public void ProcessOrder(IEmailService emailService){emailService.SendConfirmation();}
}

2.3 DI 的核心组件

.NET 的 DI 系统主要由三个部分协作完成:

  1. 服务集合 (IServiceCollection):用于注册服务。

  2. 服务提供者 (IServiceProvider):用于解析(构建)服务,是真正的容器。

  3. 构造函数:用于注入依赖

应用程序宿主IServiceCollectionIServiceProvider控制器服务启动阶段1. 注册服务:AddScoped<IService, Service>2. 构建服务提供程序:BuildServiceProvider(SC)容器根据注册信息初始化运行时阶段(每个请求)3. 创建作用域:CreateScope()返回IServiceScope4. 从作用域解析控制器:GetService<Controller>()5. 解析控制器的依赖(如IService)6. 创建服务实例(根据生命周期)7. 返回控制器实例(已注入服务)8. 调用服务方法9. 返回结果请求结束10. 释放作用域应用程序宿主IServiceCollectionIServiceProvider控制器服务
  1. 启动阶段:

    • 应用程序启动时,在ConfigureServices方法中,所有服务(包括接口和对应的实现类)都被注册到IServiceCollection中,并指定它们的生命周期(Transient、Scoped或Singleton)。

    • 然后,使用IServiceCollection构建出IServiceProvider(即DI容器)。容器会根据注册信息来管理服务的生命周期和创建。

  2. 运行时阶段(针对每个请求):

    • 当一个HTTP请求到达时,ASP.NET Core会为该请求创建一个服务作用域(IServiceScope)。

    • 在该作用域内,当需要实例化某个组件(如MVC控制器)时,容器会接管该组件的创建过程。

    • 容器检查该组件的构造函数,识别出它所依赖的服务。

    • 容器根据注册的生命周期设置,决定是创建一个新的服务实例还是重用现有的实例(如果是Singleton则重用容器根中的实例,如果是Scoped则重用当前作用域内的实例,如果是Transient则每次都创建新的实例)。

    • 容器递归地解析所有依赖,直到所有依赖都被解析完毕,然后使用这些依赖实例化目标组件。

    • 控制器(或其他组件)被实例化后,就可以使用注入的服务来处理请求。

    • 请求处理结束后,服务作用域被释放,所有在该作用域内创建的、实现了IDisposable接口的Scoped和Transient服务都会被 dispose。

这样,依赖注入容器就完成了它的角色:管理服务的生命周期并在需要时注入依赖。

2.4 依赖的生命周期

这是 DI 中至关重要的一环,它决定了对象的存活时间和复用范围。.NET DI 容器支持三种生命周期:

生命周期注册方法说明示例场景
瞬时AddTransient<T>()每次请求都会创建一个新的实例无状态的服务、轻量级服务。例如,一个简单的计算器 Calculator。
作用域AddScoped<T>()同一个作用域内(如一次 Web 请求),每次请求返回同一个实例;不同作用域则实例不同。需要在其范围内保持状态的服务。这是绝大多数应用服务的默认选择,如 DbContext(数据库上下文)。
单例AddSingleton<T>()整个应用程序生命周期内只创建一个实例,所有请求共享该实例。全局状态、缓存、配置读取器、日志服务。需要是线程安全的。

2.5 DI 在 ASP.NET Core 中的具体实现

在 Program.cs 中,整个流程非常清晰:

// 1. 创建宿主构建器,它内部已经初始化了一个 IServiceCollection
var builder = WebApplication.CreateBuilder(args);// 2. 【注册阶段】向 IServiceCollection 注册服务
// - 注册控制器相关服务(MVC)
builder.Services.AddControllers();// - 注册应用自定义服务
builder.Services.AddScoped<IOrderService, OrderService>(); // 作用域服务
builder.Services.AddSingleton<ILoggerService, FileLoggerService>(); // 单例服务
builder.Services.AddTransient<IEmailValidator, EmailValidator>(); // 瞬时服务// 3. 【构建阶段】构建 IServiceProvider
var app = builder.Build(); // 这里内部会调用 builder.Services.BuildServiceProvider()// 4. 【配置中间件管道】
app.UseRouting();
app.UseAuthorization();// 5. 【映射端点】当请求到来时,路由引擎会决定由哪个控制器处理
app.MapControllers();// 6. 【运行】
app.Run();

当一个 HTTP 请求到达时:

  1. 服务器接收请求,创建 HttpContext。

  2. 中间件管道开始处理。当到达 EndpointMiddleware 时,它知道要调用某个 Controller 的 Action 方法。

  3. 它向根容器(或从根容器创建的一个作用域)请求解析该 Controller 类型。

  4. 容器开始工作,分析 Controller 的构造函数(如 public HomeController(IOrderService orderService))。

  5. 容器接着去解析 IOrderService,发现它被映射到 OrderService。

  6. 容器分析 OrderService 的构造函数,递归地解析它的所有依赖,直到整个对象树构建完毕。

  7. 容器最终将完全构建好的 Controller 实例返回给中间件。

  8. Controller 的 Action 方法被调用,它使用已注入的 IOrderService 来完成业务逻辑。

  9. 请求处理结束后,如果创建了作用域,则该作用域被释放,其中所有的 IDisposable 资源会被处理。

2.6 DI 工作流程

工作流程:

  1. 注册:在启动时,将所有服务(接口、实现、生命周期)告知 IServiceCollection。

  2. 构建:将 IServiceCollection 转换为 IServiceProvider(容器)。

  3. 解析:在运行时,容器负责递归地分析构造函数、创建实例并注入所有依赖。

数据流转
数据(对象实例)的创建和传递完全由容器控制,从最底层的依赖开始构建,最终组合成所需的目标对象,并通过构造函数注入。

总结关系:IoC 是目的(反转控制权),DI 是手段(通过注入实现控制反转),而 IoC 容器是工具(自动化实现 DI)。


文章转载自:

http://TldJUBFQ.hjwxm.cn
http://3uGSYN31.hjwxm.cn
http://dj11nYXB.hjwxm.cn
http://4UJq5sYF.hjwxm.cn
http://NgZS9HEf.hjwxm.cn
http://8jQ69mqv.hjwxm.cn
http://cg1Wjcfw.hjwxm.cn
http://nSmYnBbk.hjwxm.cn
http://Bteu7mWg.hjwxm.cn
http://5PZ0Kpqa.hjwxm.cn
http://Rty56F5A.hjwxm.cn
http://hWStmfZV.hjwxm.cn
http://rbEiBLxz.hjwxm.cn
http://8gX88Emw.hjwxm.cn
http://5fc3Bt9W.hjwxm.cn
http://9rtjELzm.hjwxm.cn
http://daJ0FL6O.hjwxm.cn
http://dX2rfVat.hjwxm.cn
http://DSuKLJZx.hjwxm.cn
http://GOE4CskH.hjwxm.cn
http://7ucXmAyU.hjwxm.cn
http://2mmPWIlV.hjwxm.cn
http://u6FB1jyF.hjwxm.cn
http://bwXfkdty.hjwxm.cn
http://YYPWR6fS.hjwxm.cn
http://ziUMxK7R.hjwxm.cn
http://YJjXUbUf.hjwxm.cn
http://hgKRsIAN.hjwxm.cn
http://QaOBEMyX.hjwxm.cn
http://oXMXp8pQ.hjwxm.cn
http://www.dtcms.com/a/381197.html

相关文章:

  • GFSK调制解调介绍(蓝牙GFSK BT=0.5)
  • 【202509新版】Hexo + GitHub Pages 免费部署个人博客|保姆级教程 第二部
  • 【算法--链表】147.对链表进行插入排序--通俗讲解
  • 亚马逊产品转化怎么提高?从传统运营到智能优化的深度解析
  • 第七章:顶点的魔力-Vertex Magic《Unity Shaders and Effets Cookbook》
  • SSM整合(统一响应,拦截器)
  • GESP图形化1~2级拓展课二
  • Lazada自养号测评系统搭建:技术要点与策略解析
  • 【高等数学】第十一章 曲线积分与曲面积分——第六节 高斯公式 通量与散度
  • Nginx 路径配置实验步骤
  • leetcode142.环形链表II
  • 【Python】家庭用电数据分析Prophet预测
  • std::thread是可以被std::move吗?
  • Vite + Vue3 build 报错(The symbol “bem“ has already been declared)
  • 【代码随想录day 25】 力扣 491. 递增子序列
  • Kanji Dojo,一款日语学习工具
  • 机器人检验报告包含内容
  • .gitignore文件的作用及用法
  • numpy数组的升维和降维的方法集锦
  • IP验证学习之agent编写
  • Redis 安全机制:从漏洞防御到生产环境加固
  • Linux多线程概念
  • 笛卡尔参数化直线霍夫变换 Hough Transform for lines with cartesian parameterisation
  • 动态代理1
  • 《2025年AI产业发展十大趋势报告》五十三
  • 高系分二,数学与工程基础
  • 9-15、AI大模型数学基础知识手册与记忆宫殿
  • DataCollatorForLanguageModeling 标签解析(92)
  • 系统编程day08-存储映射与共享内存
  • 【Webpack】模块联邦