依赖注入(Dependency Injection,简称 DI)是一种设计模式,用于实现对象之间的依赖关系解耦。它是**控制反转(Inversion of Control,IoC)**的一种具体实现方式。依赖注入的核心思想是:将对象的依赖(即它所需要的其他对象或服务)从外部注入,而不是由对象自己去创建或查找这些依赖。
以下是对依赖注入的详细解释:
什么是依赖?
在面向对象编程中,一个类(或对象)通常需要依赖其他类(或对象)来完成其功能。例如,一个 OrderService 类可能需要依赖 OrderRepository 来访问数据库。这种依赖关系如果由 OrderService 内部直接创建(如通过 new OrderRepository()),就会导致紧耦合:
- 如果
OrderRepository的实现发生变化,OrderService需要修改代码。 - 测试时无法方便地替换
OrderRepository为 mock 对象。
依赖注入的目标是通过将依赖关系的控制权交给外部(通常是一个容器或框架),从而解决这些问题。
依赖注入的定义
依赖注入是指通过外部机制(而不是类内部)将一个对象所需的依赖传递给它。换句话说,类不再负责创建或管理它的依赖,而是由外部“注入”进来。
依赖注入的三个核心角色
- 依赖(Dependency):被注入的对象或服务,例如
OrderRepository。 - 消费者(Client):需要依赖的类,例如
OrderService。 - 注入者(Injector):负责将依赖注入到消费者的实体,通常是一个 IoC 容器(如 Spring 的 ApplicationContext)或手动编写的代码。
依赖注入的实现方式
依赖注入有三种常见的注入方式:
1. 构造器注入(Constructor Injection)
- 方式:通过类的构造函数将依赖传递进来。
- 特点:
- 依赖在对象创建时一次性注入。
- 依赖不可变(通常是 final),强制要求依赖不可为空。
- 示例:
public class OrderService { private final OrderRepository orderRepository; public OrderService(OrderRepository orderRepository) { this.orderRepository = orderRepository; } public void createOrder(Order order) { orderRepository.save(order); } }- 使用时:
OrderService service = new OrderService(new OrderRepositoryImpl());
- 使用时:
2. Setter 注入(Setter Injection)
- 方式:通过 Setter 方法注入依赖。
- 特点:
- 依赖可以在对象创建后动态改变。
- 适合可选依赖或需要运行时替换的情况。
- 示例:
public class OrderService { private OrderRepository orderRepository; public void setOrderRepository(OrderRepository orderRepository) { this.orderRepository = orderRepository; } public void createOrder(Order order) { orderRepository.save(order); } }- 使用时:
OrderService service = new OrderService(); service.setOrderRepository(new OrderRepositoryImpl());
- 使用时:
3. 接口注入(Interface Injection)
- 方式:通过定义一个接口,消费者实现该接口,注入者通过接口方法注入依赖。
- 特点:较少使用,复杂度较高,但在一些特定场景下灵活。
- 示例:
public interface Injector { void injectOrderRepository(OrderRepository orderRepository); } public class OrderService implements Injector { private OrderRepository orderRepository; @Override public void injectOrderRepository(OrderRepository orderRepository) { this.orderRepository = orderRepository; } public void createOrder(Order order) { orderRepository.save(order); } }
依赖注入的优点
- 解耦合:消费者与依赖的具体实现分离,只依赖抽象(接口),提高了代码的灵活性。
- 可测试性:通过注入 mock 对象,方便进行单元测试。
- 可维护性:修改依赖实现时无需更改消费者代码。
- 可重用性:依赖可以被多个消费者共享。
依赖注入的缺点
- 复杂度增加:手动实现依赖注入可能导致代码量增加,需要额外的配置或框架支持。
- 学习成本:理解 IoC 和 DI 的概念需要一定时间。
- 运行时错误:如果依赖未正确注入,可能在运行时才暴露问题。
依赖注入与 IoC 容器的关系
在现代开发中,依赖注入通常由 IoC 容器实现,例如:
- Spring Framework:通过
@Autowired、@Inject等注解或 XML 配置自动注入依赖。 - Java CDI:通过
@Inject注解实现。 - ASP.NET Core:通过内置的 DI 容器注册和解析依赖。
示例(Spring 中的构造器注入):
@Service
public class OrderService {
private final OrderRepository orderRepository;
@Autowired
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void createOrder(Order order) {
orderRepository.save(order);
}
}
Spring 的 IoC 容器会在运行时自动创建并注入 OrderRepository 的实例。
依赖注入的实际应用场景
- 替换实现:例如将
OrderRepository从 MySQL 实现切换到 MongoDB 实现,只需注入不同的实现类。 - 单元测试:注入 mock 对象(如使用 Mockito)测试
OrderService,无需真实的数据库。 - 模块化设计:在 DDD 中,领域层通过接口定义依赖(如仓储接口),由基础设施层注入具体实现。
总结
依赖注入是一种通过外部注入依赖来实现控制反转的设计模式,它通过构造器、Setter 或接口等方式将依赖传递给消费者。它的核心价值在于解耦、增强可测试性和可维护性,常与 IoC 容器结合使用,是现代软件开发(如 Spring、.NET)中的重要实践。在 DDD 分层架构中,依赖注入也广泛用于确保领域层与基础设施层的隔离。
1586

被折叠的 条评论
为什么被折叠?



