フィールドインジェクションとは正確には何であり、それを回避する方法は?


130

フィールドインジェクションは推奨されないというSpring MVCおよびポートレットに関するいくつかの投稿を読みました。私が理解しているように、フィールドインジェクションとは、次の@AutowiredようなBeanをインジェクトすることです。

@Component
public class MyComponent {
    @Autowired
    private Cart cart;
}

私の研究中に、コンストラクター注入についても読みました:

@Component
public class MyComponent {
    private final Cart cart;

    @Autowired
    public MyComponent(Cart cart){
       this.cart = cart;
    }
}

これらのタイプの注射の両方の長所と短所は何ですか?


EDIT 1:この質問はの重複としてマークされているとおり、この質問私はそれをチェックします。質問にも回答にもコード例がないため、使用している注入タイプを推測して正しいかどうかはわかりません。


3
フィールドインジェクションがあなたの説明と同じくらい悪い場合、なぜSpringはそれを許可するのですか?フィールドインジェクションには、コードをより読みやすく冗長にしないという独自の利点があります。コーディングに十分な規律がある場合は、フィールドインジェクションを使用しても問題が発生しないことを確認できます。

@ashesそれは当時はきちんとした機能であり、その影響は完全には考慮されていなかったためです。Date(int,int,int)存在するのと同じ理由。
クリリス

回答:


225

注射タイプ

依存関係をBeanに注入する方法には3つのオプションがあります。

  1. コンストラクタを介して
  2. セッターまたはその他の方法
  3. 反射を通して、フィールドに直接

オプション3を使用しています。これは@Autowired、フィールドで直接使用する場合に起こります。


注入ガイドライン

Springが推奨する一般的なガイドラインは次のとおりです(コンストラクタベースのDIまたはセッターベースのDIのセクションを参照)。

  • 必須の依存関係の場合、または不変性を目指す場合は、コンストラクター注入を使用します
  • オプションまたは変更可能な依存関係の場合は、セッター注入を使用します
  • ほとんどの場合、フィールド注入を避けます

フィールド注入の欠点

フィールド注入が嫌われる理由は次のとおりです。

  • コンストラクターインジェクションの場合のように、不変オブジェクトを作成することはできません
  • クラスはDIコンテナーと密接に結合しており、それ以外では使用できません
  • リフレクションなしでクラスをインスタンス化することはできません(たとえば、単体テストで)。それらをインスタンス化するにはDIコンテナーが必要です。これにより、テストは統合テストのようになります。
  • 実際の依存関係は外部から隠されており、インターフェース(コンストラクターまたはメソッド)には反映されません。
  • 10個ほどの依存関係を持つことは本当に簡単です。コンストラクターインジェクションを使用している場合、10個の引数を持つコンストラクターがあり、何かが怪しいことを通知します。ただし、フィールドインジェクションを使用して無期限にフィールドを追加できます。依存関係が多すぎると、クラスは通常複数のことを実行し、単一の責任の原則に違反する可能性があるという危険な状態になります。

結論

ニーズに応じて、主にコンストラクターの注入、またはコンストラクターとセッターの注入の組み合わせを使用する必要があります。フィールド注入には多くの欠点があり、回避する必要があります。フィールドインジェクションの唯一の利点は、すべての短所を上回るわけではないので、書く方が便利であることです。


参考文献

フィールドインジェクションが通常推奨されない理由についてブログ記事を書きました:フィールド依存性インジェクションは有害と見なされます。


12
「フィールドインジェクションは避けるべきである」と世界に告げるのは、一般的には良い考えではなく、またいいことでもありません。長所と短所を示し、他の人に自分を決めさせる;)多くの人は、他の経験や物事の独自の見方を持っています。
ダイエット、

6
ここではそうかもしれませんが、コミュニティが何かを思いとどまらせるために一般的な合意に達した他の場合があります。たとえば、ハンガリー記法を考えてみましょう。
Jannik

あなたはテスト容易性と依存関係の可視性としていくつかの良い点を与えますが、私はすべてに同意しません。コンストラクター注入には欠点はありませんか?コールの実際の構成を実行するクラスに注入する5つまたは6つのフィールドを持つことが望ましい場合があります。私も不変であなたに同意しません。finalフィールドがあることは、クラスが不変である必要はありません。それが好ましい。非常に違います。
davidxxx 2017年

私はあなたが「必須の依存関係のためか、を目指したときのものだと思う不変性
アレックス・テロー

1
春のドキュメントにリンクする回答の冒頭でリンクを参照していた
Vojtech Ruzicka

47

これはソフトウェア開発における終わりのない議論の1つですが、業界の主要なインフルエンサーはこのトピックについてより理解を深め、より良いオプションとしてコンストラクターインジェクションを提案し始めています。

コンストラクター注入

長所:

  • テスト性の向上。単体テストでは、モックライブラリやSpringコンテキストは必要ありません。新しいキーワードを使用して、テストするオブジェクトを作成できます。このようなテストはリフレクションメカニズムに依存しないため、常に高速です。(この質問は30分後に行われました。作成者がコンストラクターインジェクションを使用した場合は表示されませんでした)
  • 不変。依存関係がいったん設定されると、変更することはできません。
  • より安全なコード。コンストラクターの実行後、パラメーターとして渡されたものを検証できるため、オブジェクトを使用する準備が整います。オブジェクトは準備ができているかどうかに関係なく、その間に状態はありません。フィールドインジェクションを使用すると、オブジェクトが壊れやすいときに中間ステップを導入できます。
  • 必須の依存関係のより明確な表現。この問題では、フィールドインジェクションがあいまいです。
  • 開発者に設計について考えさせます。ditは、8つのパラメーターを持つコンストラクターについて書きました。これは、実際には悪いデザインとGodオブジェクトのアンチパターンの兆候です。クラスのコンストラクタまたはフィールドに8つの依存関係があるかどうかは関係ありません。常に間違っています。人々はフィールドを介するよりもコンストラクターに多くの依存関係を追加することに消極的です。これは、しばらく停止してコード構造について考える必要があるという、脳への信号として機能します。

短所:

  • より多くのコード(ただし、最新のIDEは痛みを軽減します)。

基本的に、フィールド注入はその逆です。


1
テスト可能性、はい、フィールド注入された豆をあざけるのは私にとって悪夢でした。一度、私はコントラクトインジェクションを使用したので、不要なモッ​​クを作成する必要はありませんでした
ケノビワン2018

25

好みの問題。それはあなたの決定です。

しかし、なぜコンストラクター注入を使用しないのかを説明できます。

  1. すべての@Service@Repositoryおよび@ControllerBeanにコンストラクターを実装したくありません。つまり、豆は40〜50個以上あります。新しいフィールドを追加するたびに、コンストラクターを拡張する必要があります。いいえ、必要ありません。必要はありません。

  2. Bean(サービスまたはコントローラー)に他の多くのBeanを注入する必要がある場合はどうなりますか?4つ以上のパラメーターを持つコンストラクターは非常に醜いです。

  3. CDIを使用している場合、コンストラクターは関係ありません。


編集#1:Vojtech Ruzickaは言った:

クラスの依存関係が多すぎ、おそらく単一責任の原則に違反しているため、リファクタリングする必要があります

はい。理論と現実。次に例を示しDashboardControllerます*:8080/dashboard。単一パスにマップされます。

DashboardControllerは他のサービスから多くの情報を収集して、ダッシュボード/システム概要ページに表示します。この単一のコントローラーが必要です。したがって、この1つのパス(基本認証またはユーザーロールフィルター)のみを保護する必要があります。

編集#2:誰もがコンストラクターの8つのパラメーターに集中しているので...これは実際の例-お客様のレガシーコードです。私はそれを変えました。同じ論法が4つ以上のパラメーターに当てはまります。

これは、インスタンスの構築ではなく、コードインジェクションに関するものです。


34
8つの依存関係を持つ非常に醜いコンストラクターは、何かが間違っているというレッドフラグであり、クラスには依存関係が多すぎ、おそらく単一責任の原則に違反しているため、リファクタリングする必要があるため、実際にはすばらしいです。それは実際には良いことです。
Vojtech Ruzicka

6
@VojtechRuzickaそれは確かにいいわけではありませんが、時にはそれを避けることはできません。
ダイエット、

4
私の経験則は、40-50はもちろんのこと、3の目安です。どのクラスの依存関係も、リファクタリングする必要があることを示しているはずです。40の依存関係を持つクラスが単一責任主体またはオープン/クローズ主体に固執する方法はありません。
アミンJ

4
@AminJルールは素晴らしいですが、現実は異なります。私が働いている会社は20年以上あり、多くのレガシーコードがあります。リファクタリングは良い考えですが、費用がかかります。また、なぜそれが言っているのかわかりませんが、40-50の依存関係を意味していません。つまり、40-50のBean、コンポーネント、モジュールを意味します...
ダイエット

7
@dit、あなたの状況は明らかに技術的負債があなたに次善の選択をさせている状況です。あなた自身の言葉で言えば、あなたの意思決定は20年以上前のレガシーコードに大きく影響されている状況にあります。新しいプロジェクトを始めるとき、コンストラクター注入よりもフィールド注入をお勧めしますか?多分あなたは答えに警告を入れて、どの場合にフィールドインジェクションを選択するかを示す必要があります。
Umar Farooq Khawaja 2018

0

もう1つのコメント-Vojtech Ruzickaは、Springは次の3つの方法で豆を注入すると述べています(ポイント数が最も多い答え)。

  1. コンストラクタを介して
  2. セッターまたはその他の方法
  3. 反射を通して、フィールドに直接

この答えは間違っています-あらゆる種類の噴射に対して、春は反射を使用するためです!IDEを使用して、セッター/コンストラクターにブレークポイントを設定し、チェックします。

これは好みの問題になる場合がありますが、CASEの問題になる場合もあります。@dieterは、フィールドインジェクションが優れている優れたケースを提供しました。Springコンテキストを設定している統合テストでフィールドインジェクションを使用している場合-クラスのテスト可能性を含む引数も無効です-統合テストに後でテストを記述したい場合を除きます;)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.