依赖注入(Dependency Injection, DI)

 是现代软件开发中的一个核心概念,尤其在构建可维护、可测试、松耦合的系统时显得非常重要。依赖注入是一种设计模式,用于将对象所依赖的其他对象(依赖项)通过外部方式传入,而不是在对象内部自己创建依赖。

通常有三种主要的注入方式(模式),适用于不同的场景和需求。

模式适用场景优点缺点
构造函数注入强依赖,核心依赖明确依赖,便于测试构造函数参数多时不易维护
属性注入可选依赖,插件式设计灵活可能出现未初始化的依赖
方法注入临时依赖,工具类简洁,不影响类结构依赖不易复用,测试略复杂

构造函数注入(Constructor Injection)

🎯 场景:日志记录服务

我们有一个 ILogger 接口,有两个实现类:ConsoleLoggerFileLogger。我们将通过构造函数将 ILogger 注入到业务类中。


✅ 第一步:定义接口和实现类
public interface ILogger
{
    void Log(string message);
}

public class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine($"[Console] {message}");
    }
}

✅ 第二步:业务类使用构造函数注入
public class OrderService
{
    private readonly ILogger _logger;

    // 构造函数注入
    public OrderService(ILogger logger)
    {
        _logger = logger;
    }

    public void PlaceOrder()
    {
        // 业务逻辑
        _logger.Log("订单已成功创建。");
    }
}

✅ 第三步:手动注入依赖并运行
class Program
{
    static void Main(string[] args)
    {
        ILogger logger = new ConsoleLogger(); // 创建依赖
        OrderService service = new OrderService(logger); // 注入依赖

        service.PlaceOrder();
    }
}

🧠 构造函数注入的优点
  • 强制依赖:必须在构造时提供依赖,避免运行时出错。
  • 不可变性:依赖通常是 readonly 字段,增强安全性。
  • 适合核心依赖:如数据库连接、日志服务等。

属性注入(Property Injection)

属性注入是通过设置公共属性来注入依赖。

✅ 示例代码:

public interface ILogger
{
    void Log(string message);
}

public class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine($"[Log] {message}");
    }
}

public class ReportService
{
    // 属性注入
    public ILogger Logger { get; set; }

    public void GenerateReport()
    {
        Logger?.Log("Report generated.");
    }
}

使用方式:

var service = new ReportService();
service.Logger = new ConsoleLogger(); // 手动注入
service.GenerateReport();

在使用依赖注入容器时,属性注入通常需要额外配置或使用特定框架支持(如 Autofac、Unity)。

方法注入(Method Injection)

方法注入是通过方法参数传入依赖。

✅ 示例代码:

public class NotificationService
{
    public void Notify(string message, ILogger logger)
    {
        logger.Log($"Notification: {message}");
    }
}

使用方式:

var logger = new ConsoleLogger();
var service = new NotificationService();
service.Notify("System update completed.", logger);

 依赖注入的生命周期

生命周期类型创建时机生命周期范围适用场景
Singleton(单例)第一次请求时创建应用程序整个生命周期配置类、缓存、日志等
Scoped(作用域)每个请求创建一次每个 HTTP 请求或作用域Web 应用中每个请求独立的数据
Transient(瞬时)每次请求都创建无状态、短生命周期轻量级服务、无状态逻辑

Singleton

					// 1、创建依赖注入(IOC容器)
					ServiceCollection services = new ServiceCollection();

 					// 2、注册ProductService
					services.AddSingleton<ProductService>();
					// 2.1、注册ProductRepository
					services.AddSingleton<ProductRepository>();

					// 3、取对象(构造ServiceProvider)
					var ServiceProvider = services.BuildServiceProvider();

					// 4、取对象
					ProductService productService = ServiceProvider.GetRequiredService<ProductService>();
productService.GetProduct();

Transient

// 1、创建依赖注入(IOC容器)
ServiceCollection services = new ServiceCollection();

// 2、注册ProductService
services.AddTransient<ProductService>();
// 2.1、注册ProductRepository
services.AddTransient<ProductRepository>();

// 3、取对象(构造ServiceProvider)
var ServiceProvider = services.BuildServiceProvider();

// 4、取对象
ProductService productService = ServiceProvider.GetRequiredService<ProductService>();
productService.GetProduct();

// 4、取对象(验证Transient)
ProductService productService1 = ServiceProvider.GetRequiredService<ProductService>();
productService1.GetProduct();

Scoped

// 1、创建依赖注入(IOC容器)
ServiceCollection services = new ServiceCollection();

// 2、注册ProductService
services.AddScoped<IProductService,ProductService>();
// 2.1、注册ProductRepository
services.AddScoped<IProductRepository,ProductRepository>();

// 3、取对象(构造ServiceProvider)
var ServiceProvider = services.BuildServiceProvider();

		// 4、取对象
using (IServiceScope serviceScope = ServiceProvider.CreateScope()) 
{
     // 1、第一次
     IProductService productService = serviceScope.ServiceProvider.GetRequiredService<IProductService>();
     productService.GetProduct();

     // 1、第一次
     IProductService productService1 = serviceScope.ServiceProvider.GetRequiredService<IProductService>();
     productService1.GetProduct();
}

依赖注入:使用Autofac

属性依赖注入

// 1、创建ContainerBuilder
ContainerBuilder containerBuilder = new ContainerBuilder();

containerBuilder.RegisterType<ProductService>().As<IProductService>().PropertiesAutowired();
containerBuilder.RegisterType<CSProductService>().As<IProductService>().PropertiesAutowired();
containerBuilder.RegisterType<ProductRepository>().As<IProductRepository>();

// 2、构造容器
var Container = containerBuilder.Build();

// 3、取对象
using (var scope = Container.BeginLifetimeScope()) {
   IProductService productService = scope.Resolve<IProductService>();
   productService.GetProduct();
}  

批量注册

// 1、创建ContainerBuilder
 ContainerBuilder containerBuilder = new ContainerBuilder();

// 1.1、整个项目注册
                containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())// 加载整个项目
                                .AsImplementedInterfaces() // 只要有接口都实现注册
                                .PropertiesAutowired(); // 开发属性依赖注入

// 2、构造容器
var Container = containerBuilder.Build();

 // 3、取对象
using (var scope = Container.BeginLifetimeScope())
{
     IProductService productService = scope.Resolve<IProductService>();
     productService.GetProduct();

     IOrderService orderService = scope.Resolve<IOrderService>();
     orderService.GetOrder();
}

对象过滤

// 1、创建ContainerBuilder
ContainerBuilder containerBuilder = new ContainerBuilder();

// 1.1、整个项目注册
containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())// 加载整个项目
    .Where(t=> t.GetInterfaces().Any(i => i == typeof(IInject))) // 实现过滤
    .AsImplementedInterfaces() // 只要有接口都实现注册
    .PropertiesAutowired(); // 开发属性依赖注入

// 2、构造容器
var Container = containerBuilder.Build();

// 3、取对象
using (var scope = Container.BeginLifetimeScope())
{
    IProductService productService = scope.Resolve<IProductService>();
    productService.GetProduct();

    IOrderService orderService = scope.Resolve<IOrderService>();
    orderService.GetOrder();
}

🌟 依赖注入的重要性

1. 降低耦合度

不依赖具体实现,而是依赖接口(或抽象类)。

类之间的耦合降低,变得更灵活、更易于替换实现。

🔧 示例:

// 不用 DI 的代码(高耦合)
var logger = new FileLogger();  // 硬编码依赖

// 用 DI 的代码(低耦合)
public class MyService
{
    private readonly ILogger _logger;
    public MyService(ILogger logger) // 外部注入
    {
        _logger = logger;
    }
}

2. 提高测试性(支持单元测试/Mock)

可以方便地替换依赖,比如用模拟对象(Mock)替代真实服务,便于单元测试。

🧪 示例:
注入一个假的数据库或服务,从而隔离测试逻辑,不依赖真实的外部系统。


3. 增强可维护性

修改依赖实现时,不需要修改使用它的类,只需修改配置或注入部分。


4. 支持开闭原则(OCP)

类对扩展开放,对修改关闭。通过依赖注入,可以新增实现而不需要修改原来的代码。


5. 易于管理生命周期

通过依赖注入容器(如 .NET 的 Microsoft.Extensions.DependencyInjection 或 Java 的 Spring),可以集中管理对象的创建和销毁。


🚀 应用场景

Web 应用程序控制器依赖服务组件。

日志记录、数据库访问、缓存管理等常用服务。

插件化系统中的动态模块加载。

使用 IoC 容器(如 Autofac、Unity、Spring、Guice 等)时。


💬 总结一句话:

依赖注入不是为了减少代码量,而是为了让代码更可扩展、可维护、可测试、可复用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值