引言:什么是依赖注入以及为什么在Java中需要它
在现代Java开发中,代码的整洁性和可测试性是对专业开发者的关键要求之一。Dependency Injection (DI),即依赖注入,是一种设计模式,它能让你的代码实现松耦合、灵活且易于维护。对象不再自行创建其依赖(例如通过new),而是由DI从外部“注入”这些依赖。这是控制反转(IoC)原则的基础部分。
在本文中,我们将解析Dependency Injection在Java中的工作原理,探讨三种主要的实现方式(通过构造器、setter方法和接口),并展示DI如何应用于实际项目中,包括流行的Spring框架。
为什么没有DI你的代码会变得脆弱?
想象一个Car类,它在内部创建了一个引擎:
public class Car {
private Engine engine;
public Car() {
this.engine = new PetrolEngine(); // 硬编码耦合
}
public void start() {
engine.run();
}
}
这种方式的明显问题包括:
- 无法复用:你无法轻松地将
PetrolEngine替换为ElectricEngine。 - 测试困难:进行单元测试时,你必须创建真实的引擎,而不是它的模拟对象(mock)。
- 违反单一职责原则(SRP):
Car类不仅负责管理汽车,还负责创建引擎。
Dependency Injection通过将依赖的创建委托给外部代码(容器或其他类)来解决这些问题。
Java中依赖注入的三种方式
在Java中,实现DI主要有三种方法。我们以通知系统为例来逐一说明。
1. 构造器注入(Constructor Injection)
这是最推荐的方式。依赖通过构造器参数传递。这保证了对象总是在有效状态下被创建,并且依赖在创建后不能被修改(不可变性)。
public interface MessageService {
void sendMessage(String message, String recipient);
}
public class EmailService implements MessageService {
@Override
public void sendMessage(String message, String recipient) {
System.out.println("Email sent to " + recipient + ": " + message);
}
}
public class NotificationSender {
private final MessageService messageService;
// 通过构造器注入依赖
public NotificationSender(MessageService messageService) {
this.messageService = messageService;
}
public void notifyUser(String userEmail, String msg) {
messageService.sendMessage(msg, userEmail);
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
MessageService emailService = new EmailService();
NotificationSender sender = new NotificationSender(emailService); // DI
sender.notifyUser("user@example.com", "Hello!");
}
}
2. Setter方法注入(Setter Injection)
适用于可选的依赖,或者依赖可能在对象生命周期内被修改的情况。但是,如果忘记调用setter方法,对象可能会在不完整的状态下被创建。
public class NotificationSender {
private MessageService messageService;
// 用于注入依赖的setter方法
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
public void notifyUser(String userEmail, String msg) {
if (messageService != null) {
messageService.sendMessage(msg, userEmail);
} else {
System.o