一、什么是依赖注入?
例如:
OrderService
需要用Logger
记录日志,那么OrderService
依赖于Logger
。简单说就是 "谁需要依赖,就由外部把依赖传给它",而非自己创建。
二、为什么需要依赖注入?


// 日志类 public class Logger {public void Log(string message) => Console.WriteLine($"日志:{message}"); }// 订单服务(依赖Logger) public class OrderService {// 自己创建依赖(问题所在)private readonly Logger _logger = new Logger();public void CreateOrder(){_logger.Log("订单创建成功");// 其他业务逻辑... } }
这个例子中OrderService
直接依赖Logger
的具体实现,如果未来要替换为FileLogger
,必须修改OrderService,这就违反了开闭原则,对扩展开放,对修改关闭
如果使用依赖注入,应该要生命一个抽象接口,让服务类去依赖接口,以后要添加别的类型的日志的适合直接添加就行,然后在逻辑代码中实例化接口,传入给服务类的构造函数。


// 抽象接口(依赖抽象) public interface ILogger {void Log(string message); }// 具体实现 public class ConsoleLogger : ILogger {public void Log(string message) => Console.WriteLine($"日志:{message}"); }// 订单服务(依赖通过外部注入) public class OrderService {private readonly ILogger _logger;// 构造函数注入(推荐方式)public OrderService(ILogger logger){_logger = logger; // 依赖由外部传入,而非自己创建 }public void CreateOrder(){_logger.Log("订单创建成功");} }
三、依赖注入的 3 种方式
1. 构造函数注入(最推荐)


public class UserService {private readonly ILogger _logger;// 构造函数注入:创建UserService时必须提供ILoggerpublic UserService(ILogger logger){_logger = logger ?? throw new ArgumentNullException(nameof(logger));} }
2. 属性注入
在ASP.NET Core 中,通常配合
[Inject]
特性使用(如在视图中)

public class UserService {// 属性注入:依赖是可选的public ILogger Logger { get; set; }public void DoWork(){Logger?.Log("执行工作"); // 注意判空 } }
3. 方法注入


public class UserService {public void DoWork(ILogger logger){logger.Log("执行工作"); // 仅该方法需要依赖 } }
四、微软自带的Microsoft.Extensions.DependencyInjection
1.核心原理是通过服务容器管理服务的注册、生命周期和依赖解析,其设计遵循 “依赖倒置” 和 “控制反转” 原则,核心流程可分为服务注册、容器构建和服务解
using Microsoft.Extensions.DependencyInjection;// 定义服务接口和实现 public interface IUserService { } public class UserService : IUserService { }// 注册服务其实就是一个LIst<ServiceDescriptor>集合 var services = new ServiceCollection(); // 瞬时生命周期,new一个新的元素,参数:抽象类型为接口,具体实现类型,Transient是生命周期 services.AddTransient<IUserService, UserService>(); // 作用域生命周期 services.AddScoped<IUserService, UserService>(); // 单例生命周期 services.AddSingleton<IUserService, UserService>();
// 3. 构建服务提供器(DI容器)
_serviceProvider = services.BuildServiceProvider();
2.容器构建(BuildServiceProvider
)
当服务注册完成后,调用 services.BuildServiceProvider()
会创建 IServiceProvider
实例(默认实现为 ServiceProvider
)。这一步是 “从注册到可用容器” 的关键转换,核心工作包括:
验证服务注册的合法性
为每个服务创建 “解析器”
初始化根容器与作用域管理
3.服务解析(GetService
/GetRequiredService
)
当调用 serviceProvider.GetRequiredService<T>()
时,ServiceProvider
会按以下流程解析服务实例:
查找服务元数据
根据生命周期创建实例
自动注入依赖(构造函数注入)
处理 IDisposable
服务
4.最后总结原理就类似 :
1)注册阶段:new一个字典集合,注册:相当于添加元素,添加接口和实现类指定生命周期方式
2)构建阶段:BuildServiceProvider()
验证注册合法性,生成解析器,初始化根容器。
3)解析阶段:
据服务类型查找 ServiceDescriptor
。按生命周期(Transient/Scoped/Singleton)创建实例,递归注入依赖。缓存实例(Scoped 缓存于作用域,Singleton 缓存于根容器)。
4)清理阶段:作用域或根容器释放时,自动清理 IDisposable
服务。