使用 Guice 通过构造函数注入和通过字段注解注入有什么区别?
What is the difference between injection through the constructor and injection through field annotations with Guice?
我有以下代码使用 Guice 进行依赖注入。第一个是使用构造函数注入,而另一个是直接在字段上方添加 @Inject
。这两种方式有什么区别吗? Guice官网上好像推荐构造函数注入。
class BillingService {
private final CreditCardProcessor processor;
private final TransactionLog transactionLog;
@Inject
BillingService(CreditCardProcessor processor, TransactionLog transactionLog) {
this.processor = processor;
this.transactionLog = transactionLog;
}
...
}
并且:
class BillingService {
@Inject
private final CreditCardProcessor processor;
@Inject
private final TransactionLog transactionLog;
BillingService() {
}
...
}
这是一个区别。在后一种情况下,注入是您可以完全构造 BillingService
实例的唯一方法。如果出于某种原因你需要构建一个没有注入的,你不能(至少使用所示的方法)。
在前一种情况下,如果您有某种原因想要这样做,您仍然可以用老式的方式构建一个:
new BillingService(someProcessor, someLog);
我曾在一个团队工作过,一个团队以这种方式做事,另一个团队则以另一种方式做。在大多数情况下,我一直使用注入,即使是为了测试。但偶尔在单元测试中,我发现以非 Guicy 方式构造一些东西很方便,在这些情况下,构造函数注入确实为你带来了灵活性。
我要指出的差异:
- 如果没有构造函数注入,您将无法使用
final
修饰符,即您上面的代码将无法编译。在这里评论 final 成员的优点是题外话。
- 对于构造函数注入,所有依赖项都是强制性的。如果不知道每个声明的依赖项,您将无法实例化 class。
- 使用构造函数注入编写测试用例可能更容易(参见 The111 的答案)。
- 还有另一种类型的 DI - setter 注入 - 它可以更自然地与构造函数注入混合使用(例如,用于分离强制和可选依赖项)。
我有以下代码使用 Guice 进行依赖注入。第一个是使用构造函数注入,而另一个是直接在字段上方添加 @Inject
。这两种方式有什么区别吗? Guice官网上好像推荐构造函数注入。
class BillingService {
private final CreditCardProcessor processor;
private final TransactionLog transactionLog;
@Inject
BillingService(CreditCardProcessor processor, TransactionLog transactionLog) {
this.processor = processor;
this.transactionLog = transactionLog;
}
...
}
并且:
class BillingService {
@Inject
private final CreditCardProcessor processor;
@Inject
private final TransactionLog transactionLog;
BillingService() {
}
...
}
这是一个区别。在后一种情况下,注入是您可以完全构造 BillingService
实例的唯一方法。如果出于某种原因你需要构建一个没有注入的,你不能(至少使用所示的方法)。
在前一种情况下,如果您有某种原因想要这样做,您仍然可以用老式的方式构建一个:
new BillingService(someProcessor, someLog);
我曾在一个团队工作过,一个团队以这种方式做事,另一个团队则以另一种方式做。在大多数情况下,我一直使用注入,即使是为了测试。但偶尔在单元测试中,我发现以非 Guicy 方式构造一些东西很方便,在这些情况下,构造函数注入确实为你带来了灵活性。
我要指出的差异:
- 如果没有构造函数注入,您将无法使用
final
修饰符,即您上面的代码将无法编译。在这里评论 final 成员的优点是题外话。 - 对于构造函数注入,所有依赖项都是强制性的。如果不知道每个声明的依赖项,您将无法实例化 class。
- 使用构造函数注入编写测试用例可能更容易(参见 The111 的答案)。
- 还有另一种类型的 DI - setter 注入 - 它可以更自然地与构造函数注入混合使用(例如,用于分离强制和可选依赖项)。