web-dev-qa-db-ja.com

フィールドインジェクションとは何ですか?それを避ける方法は?

Spring MVCおよびPortletsに関するいくつかの投稿で、フィールドインジェクションは推奨されていません。原因を取得しようとしているので、フィールドインジェクションを使用しているかどうかを自問しましたが、答えられません。 フィールドインジェクションは、次のように@Autowiredを使用してBeanを属性にインジェクトする場合です。

CartController.Java:

...
@Autowired
private Cart cart;
...

BookshopConfiguartion.Java:

@Configuration
public class BookShopConfiguration {

@Bean
public Cart cart(){
    return new Cart();
}
//more configuration

Cart.Javaは、カート内の本に関する情報を保存および提供するために使用されます。

私の研究中にconstructor injectionについて読みました。

MyComponent.Java:

...
public class MyComponent{
private Cart cart;

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

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


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

72
T. Jung

注入タイプ

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

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

オプション3を使用しています。フィールドで@Autowiredを直接使用すると、これが発生します。


注入ガイドライン

一般的なガイドライン Springが推奨Constructor-based DI または Setter-based DI のセクションを参照)は次のとおりです。

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

フィールドインジェクションの欠点

フィールドインジェクションが嫌われる理由は次のとおりです。

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

結論

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


さらに読む

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

131
Vojtech Ruzicka

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

コンストラクターインジェクション

長所:

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

短所:

  • その他のコード(ただし、最新のIDEは痛みを軽減します)。

基本的に、フィールド注入は反対です。

32

味の問題。それはあなたの決断です。

しかし、なぜコンストラクターインジェクションを使用しないのか説明できます.

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

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

  3. CDIを使用している場合、コンストラクタは気にしません。


EDIT:Vojtech Ruzicka氏:

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

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

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

21
dieter
Field
@Autowired
private DependencyA dependencyA;

@Autowired
private DependencyB dependencyB;

@Autowired
private DependencyC dependencyC;

何が問題なのですか?ご覧のとおり、Fieldバリアントは非常に見栄えが良いです。非常に短く、簡潔で、定型的なコードはありません。コードは読みやすく、ナビゲートしやすいです。あなたのクラスは重要なことに集中することができ、DIボイラープレートによって汚染されません。 @Autowiredアノテーションをフィールドの上に置くだけで、それで終わりです。依存関係を提供するためのDIコンテナー専用の特別なコンストラクターまたはセッターはありません。 Javaは非常に冗長なので、コードを短くするあらゆる機会を歓迎しますか?

単一責任原則違反

新しい依存関係を追加するのは非常に簡単です。たぶん簡単です。 6、10、または数十の依存関係を追加しても問題ありません。 DIのコンストラクターを使用している場合、特定のポイントの後、コンストラクターのパラメーターの数が非常に多くなり、何かが間違っていることがすぐにわかります。通常、依存関係が多すぎるということは、クラスの責任が多すぎることを意味します。これは、単一責任原則および懸念の分離に違反する可能性があり、クラスがさらなる検査と可能なリファクタリングを必要とすることを示す良い指標です。このアプローチは無期限にスケーリングできるため、フィールドに直接注入する場合、このようなレッドフラグはありません。

依存関係の非表示

DIコンテナを使用すると、クラスが独自の依存関係を管理する必要がなくなります。依存関係を取得する責任は、クラスから抽出されます。現在、他の誰かが依存関係を提供する責任があります-DIコンテナー、またはテストでそれらを手動で割り当てます。クラスがその依存関係を取得する責任を負わなくなった場合、パブリックインターフェイス(メソッドまたはコンストラクター)を使用してそれらを明確に通信する必要があります。このようにして、クラスに必要なものが明確になり、オプション(セッター)か必須(コンストラクター)かが明確になります。

DIコンテナカップリング

DIフレームワークの中心的なアイデアの1つは、マネージクラスが使用されるDIコンテナに依存しないことです。言い換えれば、必要な依存関係をすべて渡せば、独立してインスタンス化できる単純なPOJOである必要があります。この方法により、DIコンテナーを開始せずに単体テストでインスタンス化し、個別にテストできます(より統合テストに近いコンテナーで)。コンテナカップリングがない場合、クラスを管理対象または非管理対象として使用するか、新しいDIフレームワークに切り替えることさえできます。

ただし、フィールドに直接注入する場合、必要なすべての依存関係でクラスをインスタンス化する直接的な方法は提供しません。つまり:

必須のコラボレーターが不足しており、使用するとNullPointerExceptionになる場合に、状態でnewを使用してオブジェクトを作成する方法があります(デフォルトコンストラクターを呼び出す)。そのようなクラスは、リフレクション以外に必要な依存関係を提供する方法がないため、DIコンテナ(テスト、他のモジュール)の外部では再利用できません。不変性コンストラクターとは異なり、フィールドインジェクションを使用して最終フィールドに依存関係を割り当て、オブジェクトを効果的に可変にすることはできません。

3
pramodgautam