web-dev-qa-db-ja.com

依存関係の注入を適切に行う方法(Spring)

Springを使用してオブジェクトをクラスに注入することには疑問があります。私は私のプロジェクトでこの種のコードを使用しました:

@Resource // or @Autowired even @Inject
private PersonRepository personRepository;

次に、メソッドで通常どおり使用しました:

personRepository.save(p);

それ以外の場合、Springの例でコンストラクタを注入しました。

private final PersonRepository personRepository;

@Autowired
public PersonController(PersonRepository personRepository) {
  this.personRepository = personRepository;
}

両方とも正しいですか?または、それぞれにプロパティと用途がありますか?

26

tl; dr-コンストラクター注入はDIを行うための最良の方法です

後者は正しいです。これは、Springや依存性注入コンテナのためではなく、オブジェクト指向のクラス設計原則のためです。

詳細

有効な状態のインスタンスのみを作成できるように、タイプを設計する必要があります。これを実現するには、そのタイプのすべての必須依存関係needをコンストラクター引数にする必要があります。これは、これらの依存関係をnull-チェックし、最終フィールドに割り当てて不変性を促進できることを意味します。それを超えて、コードを操作するとき、そのインスタンスの呼び出し元(または作成者)に対して、どの依存関係を提供する必要があるかがすぐにわかります(APIドキュメントのスキミングまたはIDEでのコード補完の使用)。

これはすべて、フィールド注入では不可能です。外部からの依存関係は表示されません。依存関係を注入するための黒魔術requireであり、それらがnullコンテナを盲目的に信頼する場合を除きます。

最後になりましたが、重要なことですが、フィールドインジェクションを使用すると、クラスに多くの依存関係を追加するのが簡単になります。これは、それ自体が設計上の問題です。コンストラクターを使用すると、クラス設計について何かを教えてくれる良いことがずっと早く痛くなります:クラスには責任が多すぎます。そもそもメトリックを計算する必要はありません。拡張しようとすると感じます。

コンテナ

とにかくコンテナに頼ることができるので、人々はしばしばそれは単なる学術のでたらめだと主張します。これについての私の見解は次のとおりです。

  • コンテナが存在するからといって、基本的なオブジェクト指向の設計原則をすべて捨てなければならないわけではありませんか?制汗剤が存在する場合でも、シャワーを浴びますか?

  • コンテナで使用するために設計されたタイプでさえ、はユニットテストで手動で使用されます。単体テストを作成しない場合は、それは別のトピックです。

  • 追加のコンストラクターの冗長性(「1行のフィールドインジェクションで同じことを実現できます!!」-「いいえ、できません。実際にはgetコードを1行以上書くことによるもの。 ") Lombok のようなものによって軽減できます。 SpringとLombokを使用して作成されたコンストラクターのコンポーネントは次のようになります。

    @Component
    @RequiredArgsConstructor
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    

Lombokは、各最終フィールドのパラメーターを取得するコンストラクターの生成を処理し、nullの指定されたパラメーターをチェックしてから割り当てます。したがって、フィールドインジェクションの簡潔さと、コンストラクタインジェクションの設計上の利点を効果的に得ることができます。

エピローグ

最近、コンストラクターDIに「インジェクション」という用語を使用することでひどく困惑した非Javaの人々との議論に参加しました。事実上、彼らは、オブジェクトを他のオブジェクトに渡す最も自然な方法であるため、コンストラクターを介して依存関係を渡すことはまったく注入ではない、と多くの真実があります(あらゆる種類の注入とはまったく対照的です)。

たぶん、私たちはその種のスタイルに対して別の用語を作るべきですか?たぶん依存性の摂食?

資源

72
Oliver Drotbohm

学問的に言えば、コンストラクタはオブジェクトを作成するためのより良い方法であることに同意します。ただし、Java Beanの仕様は、リフレクターを前提とするミューテーターに基づいて構築されています。ミューテーターは「友人だけがあなたのプライベート部分を見ることができる」という古いルールを破るので、コンストラクター注入のみを使用する必要があります。

2
Ryan McGuinness