どのクラスをSpringによって自動配線する必要がありますか(依存性注入を使用する場合)?


32

しばらくの間、SpringでDependency Injectionを使用してきましたが、それがどのように機能し、それを使用することの長所と短所を理解しています。しかし、新しいクラスを作成するとき、よく疑問に思う-このクラスはSpring IOC Containerによって管理されるべきか?

また、@ Autowiredアノテーション、XML構成、セッターインジェクション、コンストラクターインジェクションなどの違いについては説明しません。私の質問は一般的なものです。

コンバーター付きのサービスがあるとしましょう:

@Service
public class Service {

    @Autowired
    private Repository repository;

    @Autowired
    private Converter converter;

    public List<CarDto> getAllCars() {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
}

@Component
public class Converter {

    public CarDto mapToDto(List<Car> cars) {
        return new ArrayList<CarDto>(); // do the mapping here
    }
}

明らかに、コンバーターには依存関係がないため、自動接続する必要はありません。しかし、私にとっては自動配線されたほうが良いようです。コードは簡潔で、テストが簡単です。DIなしでこのコードを書くと、サービスは次のようになります。

@Service
public class Service {

    @Autowired
    private Repository repository;

    public List<CarDto> getAllCars() {
        List<Car> cars = repository.findAll();
        Converter converter = new Converter();
        return converter.mapToDto(cars);
    }
}

今ではそれをテストすることははるかに困難です。さらに、常に同じ状態にあるにもかかわらず、すべての変換操作に対して新しいコンバーターが作成されます。これはオーバーヘッドのようです。

Spring MVCには、いくつかのよく知られたパターンがあります。サービスを使用するコントローラーとリポジトリーを使用するサービス。次に、リポジトリが自動配線されている場合(通常は自動配線)、サービスも自動配線する必要があります。そして、これは非常に明確です。しかし、いつ@Componentアノテーションを使用するのでしょうか?いくつかの静的utilクラス(コンバーター、マッパーなど)がある場合-それらを自動配線しますか?

すべてのクラスを自動配線しようとしていますか?その後、すべてのクラスの依存関係を注入するのが簡単です(もう一度、理解しやすく、テストしやすい)。または、絶対に必要な場合にのみ自動配線を試みますか?

自動配線を使用するタイミングに関する一般的なルールを探すのに少し時間を費やしましたが、具体的なヒントは見つかりませんでした。通常、人々は「DIを使用しますか(はい/いいえ)」または「どのタイプの依存性注入を好みますか」について話しますが、これは私の質問には答えません。

このトピックに関するヒントに感謝します!


3
本質的に「いつ行き過ぎるのか」に対する+1
アンディハント14年

この質問とこの記事
Laiv

回答:


8

@ericWのコメントに同意し、コードをコンパクトに保つ​​ためにイニシャライザを使用できることを忘れないでください。

@Autowired
private Converter converter;

または

private Converter converter = new Converter();

または、クラスに状態がまったくない場合

private static final Converter CONVERTER = new Converter();

SpringがBeanをインスタンス化して注入する必要があるかどうかの重要な基準の1つは、そのBeanが非常に複雑であるため、テストでモックしたいということです。もしそうなら、それを注入します。たとえば、コンバータが外部システムへのラウンドトリップを行う場合、代わりにそれをコンポーネントにします。または、戻り値に、入力に基づいて可能なバリエーションが多数ある大きな決定ツリーがある場合は、それをモックします。などなど。

その機能をロールアップしてカプセル化することはすでに上手く行っているので、テスト用の別の「ユニット」と見なすのに十分なほど複雑であるかどうかは問題です。


5

すべてのクラスを@Autowiredする必要はないと思います。実際の使用法に依存するはずです。シナリオでは、@ Autowiredの代わりに静的メソッドを使用する方が良いでしょう。これらの単純なutilsクラスに@Autowiredを使用するメリットは見当たりません。適切に使用しないと、Springコンテナのコストが絶対的に増加します。


2

私の経験則は、あなたが以前から言っていた、テスト容易性に基づいています。「簡単に単体テストを実行できますか?」答えが「はい」の場合、他の理由がなければ、それで問題ありません。したがって、開発中に単体テストを開発すると、多くの苦痛が軽減されます。

唯一の潜在的な問題は、コンバーターが失敗すると、サービステストも失敗することです。一部の人々は、ユニットテストでコンバータの結果をモックするべきだと言うでしょう。これにより、エラーをより迅速に特定できるようになります。しかし、それには代償があります。実際のコンバーターが作業を完了できた場合、すべてのコンバーターの結果をモックする必要があります。

また、異なるdtoコンバーターを使用する理由はまったくないと思います。


0

TL; DR:DIの自動配線とDIのコンストラクター転送のハイブリッドアプローチにより、提示したコードを簡素化できます。

@autowired Bean初期化依存関係に関連するSpringフレームワークの起動エラー/合併症を伴ういくつかのweblogicのために、私は同様の質問を見てきました。私は別のDIアプローチ、つまりコンストラクター転送でミキシングを始めました。提示するような条件が必要です(「明らかに、コンバーターには依存関係がないため、自動配線する必要はありません。」)。それにもかかわらず、私は柔軟性のために多くのことが好きであり、それはまだかなり簡単です。

@Service
public class Service {

    @Autowired
    private Repository repository;

    public List<CarDto> getAllCars(Converter converter) {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
    public List<CarDto> getAllCars() {
        Converter converter = new Converter();
        return getAllCars(converter);
    }
}

または、ロブの答えから逃れるために

@Service
public class Service {

    @Autowired
    private Repository repository;

    private final Converter converter = new Converter(); // static if safe for that

    public List<CarDto> getAllCars(Converter converter) {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
    public List<CarDto> getAllCars() {
        return getAllCars(converter);
    }
}

パブリックインターフェイスの変更は必要ないかもしれませんが、私はそうします。それでも

public List<CarDto> getAllCars(Converter converter) { ... }

テスト目的/拡張のみを目的として、保護または非公開にすることができます。

重要な点は、DIはオプションです。デフォルトが用意されており、簡単に上書きできます。フィールドの数が増えるにつれて弱点がありますが、1つ、おそらく2つのフィールドでは、次の条件でこれを好むでしょう。

  • 非常に少数のフィールド
  • デフォルトはほとんど常に使用される(DIはテストシーム)またはデフォルトはほとんど使用されない(ギャップを止める)一貫性が好きなので、これは真の設計制約ではなく、意思決定ツリーの一部です

最後のポイント(ビットOT、しかし、どのように/どこで@autowiredを決定するかに関連):コンバータクラスは、提示されているように、ユーティリティメソッドです(フィールド、コンストラクタ、静的な可能性があります)。解決策は、Carsクラスに何らかのmapToDto()メソッドを含めることでしょうか?つまり、変換の注入をCars定義にプッシュします。

@Service
public class Service {

   @Autowired
   private Repository repository;

   public List<CarDto> getAllCars() {
    return repository.findAll().stream.map(c -> c.mapToDto()).collect(Collectors.toList()));
   }
}

-1

これの良い例は、SecurityUtilsやUserUtilsのようなものだと思います。ユーザーを管理するSpringアプリでは、次のような静的メソッドの束を持つUtilクラスが必要です。

getCurrentUser()

isAuthenticated()

isCurrentUserInRole(権限機関)

これらを自動配線したことはありません。JHipster(私はベストプラクティスのかなり良い判断として使用しています)はサポートしていません。


-2

コントローラ、サービス、リポジトリ、エンティティなど、そのクラスの機能に基づいてクラスを分離できます。コントローラーやサービスなどの目的ではなく、ロジックに他のクラスを使用できます。その場合は、@ classでそのクラスに注釈を付けることができます。これにより、そのクラスがスプリングコンテナに自動的に登録されます。登録されている場合、スプリングコンテナによって管理されます。依存関係の注入と制御の反転の概念は、その注釈付きクラスに提供できます。


3
この投稿は読みにくい(テキストの壁)。ですが、あなたは気編集をより良い形にそれをINGの?
ブヨ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.