这篇[https://odrotbohm.de/2013/11/why-field-injection-is-evil/]文章讨论了字段注入(Field Injection)和构造函数注入(Constructor Injection)的优缺点,并提倡使用构造函数注入。以下是文章的主要观点和总结:
字段注入的问题
- 容易引发空指针异常(NullpointerExceptions):
- 字段注入允许客户端创建类的实例而不提供依赖项,这可能导致对象处于无效状态。
- 示例代码: MyComponent component = new MyComponent(); component.myBusinessMethod(); // -> NullPointerException
- 隐藏依赖关系:
- 字段注入隐藏了类的依赖关系,客户端无法通过类的公共接口(如构造函数或方法签名)了解其依赖项。
- 这使得代码在跨项目共享时难以维护,因为开发者需要通过运行时错误来发现缺失的依赖。
- 测试复杂性:
- 字段注入需要通过反射来注入依赖项,这增加了测试的复杂性。
- 示例代码:MyCollaborator collaborator = ... // mock dependency MyComponent component = new MyComponent(); // 使用反射注入依赖项 component.myBusinessMethod();
构造函数注入的优点
- 强制依赖:
- 构造函数注入要求客户端在创建对象时提供所有必要的依赖项,确保对象在创建时处于有效状态。
- 示例代码:class MyComponent { private final MyCollaborator collaborator; @Inject public MyComponent(MyCollaborator collaborator) { Assert.notNull(collaborator, "MyCollaborator must not be null!"); this.collaborator = collaborator; } public void myBusinessMethod() { collaborator.doSomething(); } }
- 清晰的依赖关系:
- 构造函数注入通过类的公共接口(构造函数)明确展示了依赖项,使得代码更具可读性和可维护性。
- 便于测试:
- 构造函数注入使得测试更加简单,因为可以直接通过构造函数传递依赖项。
- 示例代码:MyCollaborator collaborator = ... // mock dependency MyComponent component = new MyComponent(collaborator); component.myBusinessMethod();
- 不可变性:
- 使用
final
修饰的字段可以确保依赖项在对象创建后不可变,提高了代码的安全性和可维护性。
- 使用
字段注入 vs 构造函数注入的对比
表格
复制
特性 | 字段注入(Field Injection) | 构造函数注入(Constructor Injection) |
---|---|---|
代码量 | 较少 | 较多(但可以通过工具如 Lombok 减少) |
安全性 | 不安全(容易引发空指针异常) | 安全(确保对象处于有效状态) |
依赖关系 | 隐藏 | 明确 |
测试复杂性 | 复杂(需要反射) | 简单(直接通过构造函数传递依赖项) |
辅助工具
- *Lombok:可以减少构造函数注入所需的样板代码。例如,使用
@RequiredArgsConstructor
自动生成构造函数:
@RequiredArgsConstructor(onConstructor = @__(@Inject))
class MyComponent {
private final @NonNull MyCollaborator collaborator;
public void myBusinessMethod() {
collaborator.doSomething();
}
}
总结
- 字段注入虽然减少了代码量,但容易引发空指针异常,隐藏依赖关系,并增加了测试的复杂性。
- 构造函数注入虽然需要编写更多代码,但提供了更高的安全性、明确的依赖关系和更好的测试支持。
- 使用工具如 Lombok 可以减少构造函数注入的样板代码,同时保留其优点。
Comments | NOTHING