web-dev-qa-db-ja.com

プロパティとコンストラクターのSpring @Autowire

したがって、Springを使用していたので、依存関係のあるサービスを記述する場合、次のようにします。

@Component
public class SomeService {
     @Autowired private SomeOtherService someOtherService;
}

同じ目標を達成するために別の規則を使用するコードを実行しました

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

これらの方法はどちらも機能します、私はそれを理解しています。しかし、オプションBを使用する利点はありますか?私にとっては、クラスと単体テストでより多くのコードを作成します。 (コンストラクタを記述できず、@ InjectMocksを使用できない)

私が見逃しているものはありますか?単体テストにコードを追加する以外に、自動配線されたコンストラクターは他に何かしますか?これは依存性注入を行うためのより好ましい方法ですか?

87
GSUgambit

はい、実際にはフィールドインジェクションよりもオプションB(コンストラクタインジェクションと呼ばれます)が推奨されており、いくつかの利点があります。

  • 依存関係が明確に識別されます。テスト時、または他の状況でオブジェクトをインスタンス化するとき(設定クラスでBeanインスタンスを明示的に作成するなど)、1つを忘れる方法はありません。
  • 依存関係を最終的にすることができ、堅牢性とスレッドセーフ性に役立ちます
  • 依存関係を設定するためにリフレクションは必要ありません。 InjectMocksは引き続き使用可能ですが、必須ではありません。自分でモックを作成し、コンストラクタを呼び出すだけでモックを挿入できます

Spring-寄稿者の一人によるより詳細な記事については このブログ投稿 を参照してください Olivier Gierke

122
JB Nizet

簡単な言葉で説明します。

Option(A)、では、(Springコンテナの外部/内部の異なるクラスの)だれでも、デフォルトのコンストラクタ(new SomeService()など)を使用してインスタンスを作成できます。これはSomeOtherServiceオブジェクト( SomeServiceの依存関係として)。

単体テストにコードを追加する以外に、自動配線されたコンストラクターは他に何かしますか?これは依存性注入を行うためのより好ましい方法ですか?

Option(B)が推奨されるアプローチは、実際にSomeService依存関係を解決せずにSomeOtherServiceオブジェクトを作成できないためです。

24
developer

Autowiredコンストラクターは、スプリングコンテナーに登録する前にカスタムコードを追加するためのフックを提供します。 SomeServiceクラスがSuperSomeServiceという名前の別のクラスを拡張し、引数として名前を受け取るコンストラクターがあるとします。この場合、Autowiredコンストラクターは正常に機能します。また、初期化する他のメンバーがある場合は、インスタンスをスプリングコンテナに返す前にコンストラクタでそれを行うことができます。

public class SuperSomeService {
     private String name;
     public SuperSomeService(String name) {
         this.name = name;
     }
}

@Component
public class SomeService extends SuperSomeService {
    private final SomeOtherService someOtherService;
    private Map<String, String> props = null;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        SuperSomeService("SomeService")
        this.someOtherService = someOtherService;
        props = loadMap();
    }
}
1

実際、私の経験では、2番目のオプションの方が優れています。 @Autowiredの必要なし。実際、フレームワークと緊密に結合されていないコードを作成する方が賢明です遅延意思決定アプローチを採用するために可能な限り試行するコードが必要です。それは可能な限り pojo であり、フレームワークを簡単に交換できるほどです。したがって、次のように、個別の構成ファイルを作成し、そこにBeanを定義することをお勧めします。

SomeService.Javaファイル内:

public class SomeService {
    private final SomeOtherService someOtherService;

    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

ServiceConfig.Javaファイル内:

@Config
public class ServiceConfig {
    @Bean
    public SomeService someService(SomeOtherService someOtherService){
        return new SomeService(someOtherService);
    }
}

実際、それについて深く技術的になりたい場合は、サイズに応じて、フィールドインジェクション@Autowired)を使用すると生じるスレッドセーフの質問(特に)があります。明らかにプロジェクトの。 これを確認してください自動配線の長所と短所 の詳細をご覧ください。実際、中核の人々は、フィールドインジェクションの代わりにコンストラクタインジェクションを使用することを実際に推奨しています。

0
Dougie T

知っておくと良い

コンストラクター呼び出しが1つだけの場合、@ Autowiredアノテーションを含める必要はありません。次に、次のようなものを使用できます。

@RestController
public class NiceController {

    private final DataRepository repository;

    public NiceController(ChapterRepository repository) {
        this.repository = repository;
    }
}

... Spring Data Repositoryインジェクションの例。

0
Daniel Perník