使用 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 注入 - 它可以更自然地与构造函数注入混合使用(例如,用于分离强制和可选依赖项)。