web-dev-qa-db-ja.com

CDIでセッターインジェクションよりコンストラクターを使用するのはなぜですか?

SOで妥当な答えが見つからなかったので、重複していないことを願っています。単純なものよりもセッターまたはコンストラクターの注入を好むのはなぜですか。

@Inject
MyBean bean;

クラスの初期化中にインジェクトされたBeanで何かを行う必要がある場合は、コンストラクターインジェクションを使用します。

public void MyBean(@Inject OtherBean bean) {
    doSomeInit(bean);
    //I don't need to use @PostConstruct now
}

それでも、それは@PostConstructメソッドとほとんど同じで、セッターインジェクションをまったく取得しません。Springや他のDIフレームワークの後の単なる遺物ではありませんか?

20
Petr Mensik

コンストラクターとプロパティの注入により、単体テストなど、CDI以外の環境でもオブジェクトを簡単に初期化するオプションが提供されます。

非CDI環境でも、コンストラクターargを渡すだけでオブジェクトを使用できます。

OtherBean b = ....;
new MyBean(b);

フィールドインジェクションを使用する場合、フィールドは通常プライベートであるため、通常はリフレクションを使用してフィールドにアクセスする必要があります。

プロパティインジェクションを使用する場合は、セッターにコードを記述することもできます。例えば。検証コードを使用するか、セッターが変更するプロパティから派生した値を保持する内部キャッシュをクリアします。何をしたいかは、実装のニーズによって異なります。

セッターとコンストラクターの注入

オブジェクト指向プログラミングでは、オブジェクトは構築後に有効な状態である必要があり、メソッドを呼び出すたびに状態が別の有効な状態に変更されます。

セッターインジェクションの場合、これは、セッターがまだ呼び出されていない場合でも、オブジェクトは構築後に有効な状態になるはずなので、より複雑な状態処理が必要になる可能性があることを意味します。したがって、プロパティが設定されていない場合でも、オブジェクトは有効な状態である必要があります。例えば。デフォルト値または nullオブジェクト を使用します。

オブジェクトの存在とプロパティの間に依存関係がある場合、プロパティはコンストラクター引数である必要があります。これにより、コードがよりクリーンになります。コンストラクターパラメーターを使用すると、依存関係が必要であることが文書化されるためです。

したがって、このようなクラスを作成する代わりに

public class CustomerDaoImpl implements CustomerDao {

  private DataSource dataSource;

  public Customer findById(String id){
     checkDataSource();

     Connection con = dataSource.getConnection();
     ...
     return customer;
  }

  private void checkDataSource(){
     if(this.dataSource == null){
         throw new IllegalStateException("dataSource is not set");
     }
  }


  public void setDataSource(DataSource dataSource){
     this.dataSource = dataSource;
  }

}

コンストラクタインジェクションを使用する必要があります

public class CustomerDaoImpl implements CustomerDao {

  private DataSource dataSource;

  public CustomerDaoImpl(DataSource dataSource){
      if(dataSource == null){
        throw new IllegalArgumentException("Parameter dataSource must not be null");
     }
     this.dataSource = dataSource;
  }

  public Customer findById(String id) {    
      Customer customer = null;
     // We can be sure that the dataSource is not null
     Connection con = dataSource.getConnection();
     ...
     return customer;
  }
}

私の結論

  • すべてのオプションの依存関係propertiesを使用します。
  • すべての必須の依存関係コンストラクター引数を使用します。

PS:私のブログ pojosとJava Beans の違いは私の結論をより詳細に説明しています。

18
René Link

CDIを使用する場合、コンストラクターまたはセッターインジェクションを使用する理由はまったくありません。質問に記載されているように、コンストラクターで実行される操作に@PostConstructメソッドを追加します。

単体テストでフィールドを挿入するにはReflectionを使用する必要があると言う人もいますが、そうではありません。モックライブラリやその他のテストツールがそれを行います。

最後に、コンストラクターインジェクションではフィールドをfinalにすることができますが、これは@Injectアノテーション付きフィールド(finalにすることはできません)のデメリットではありません。注釈の存在は、フィールドを明示的に設定するコードがないことと相まって、コンテナー(またはテストツール)によってのみ設定されることを明確にする必要があります。実際には、注入されたフィールドを再割り当てする人は誰もいません。

コンストラクターとセッターの注入は、開発者が通常手動でインスタンス化してテスト対象のオブジェクトに依存性を注入する必要があった過去に意味がありました。今日、技術は進化し、フィールドインジェクションははるかに優れたオプションです。

4
Rogério