依存性注入にSpringを使用しているシステムがあります。注釈ベースの自動配線を使用します。Bean はコンポーネント スキャンによって検出されます。つまり、コンテキスト XML には次のものが含まれます。
<context:component-scan base-package="org.example"/>
私の問題を説明するために、以下のうなずく例を作成しました。
Zooオブジェクトのコンテナである がありAnimalます。の開発者は、開発中にどのオブジェクトが含まれるZooかを知りません。Spring によってインスタンス化された具体的なオブジェクトのセットはコンパイル時に認識されますが、さまざまなビルド プロファイルが存在するため、さまざまな のセットが生成され、これらの状況下でのコードを変更してはなりません。AnimalZooAnimalAnimalZoo
の目的は、特定の に明示的に依存する必要なく、実行時にZooシステムの他の部分 (ここでは として示されているZooPatron) が一連のオブジェクトにアクセスできるようにすることです。AnimalAnimal
実際、具象Animalクラスはすべて、さまざまな Maven アーティファクトによって提供されます。これらの具象を含むさまざまなアーティファクトに依存するだけでプロジェクトのディストリビューションを組み立て、Animalコンパイル時にすべてを正しく自動配線できるようにしたいと考えています。
Animal私は、個人が に依存するようにして、 duringZooで登録メソッドを呼び出せるようにすることで、この問題を解決しようとしました (失敗しました) 。これにより、 が の明示的なリストに明示的に依存することが回避されます。Zoo@PostConstructZooAnimal
このアプローチの問題点は、すべての が登録されているZoo場合にのみ、顧客が対話を希望することです。コンパイル時に既知の s の有限セットがあり、登録はすべてライフサイクルの Spring ワイヤリング フェーズ中に行われるため、サブスクリプション モデルは不要である必要があります (つまり、実行時にs に s を追加したくありません)。)。AnimalAnimalAnimalZoo
残念ながら、 のすべての顧客はZoo単に に依存していますZoo。これは、Animalが と持っている関係とまったく同じZooです。したがって、 と のメソッド@PostConstructは任意の順序で呼び出されます。これは、以下のコード例で示されています。 on が呼び出された時点で、 が登録されていません。数ミリ秒後にすべて登録されます。AnimalZooPatron@PostConstructZooPatronAnimal
したがって、ここには 2 種類の依存関係があり、Spring で表現するのに苦労しています。の顧客は、すべてのs が入った後にZooのみ使用したいと考えています。Animal(おそらく「アーク」がより良い例だったでしょう...)
私の質問は基本的に、この問題を解決する最善の方法は何ですか?
@Component
public class Zoo {
private Set<Animal> animals = new HashSet<Animal>();
public void register(Animal animal) {
animals.add(animal);
}
public Collection<Animal> getAnimals() {
return animals;
}
}
public abstract class Animal {
@Autowired
private Zoo zoo;
@SuppressWarnings("unused")
@PostConstruct
private void init() {
zoo.register(this);
}
@Component
public static class Giraffe extends Animal {
}
@Component
public static class Monkey extends Animal {
}
@Component
public static class Lion extends Animal {
}
@Component
public static class Tiger extends Animal {
}
}
public class ZooPatron {
public ZooPatron(Zoo zoo) {
System.out.println("There are " + zoo.getAnimals().size()
+ " different animals.");
}
}
@Component
public class Test {
@Autowired
private Zoo zoo;
@SuppressWarnings("unused")
@PostConstruct
private void init() {
new Thread(new Runnable() {
private static final int ITERATIONS = 10;
private static final int DELAY = 5;
@Override
public void run() {
for (int i = 0; i<ITERATIONS; i++) {
new ZooPatron(zoo);
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
// nop
}
}
}
}).start();
}
}
public class Main {
public static void main(String... args) {
new ClassPathXmlApplicationContext("/context.xml");
}
}
出力:
There are 0 different animals.
There are 3 different animals.
There are 4 different animals.
There are 4 different animals.
... etc
承認された解決策の説明
@PostConstruct基本的に答えは次のとおりです。いいえ、 Spring の「外部」に移動するか、その動作を変更しない限り、呼び出しの順序を保証することはできません。
ここでの本当の問題は、呼び出しを順序付けしたかったことではなく、依存関係が正しく表現されてい@PostConstructないという単なる兆候でした。
の消費者がZoo彼に依存し、さらにs にZoo依存している場合Animal、すべてが正しく機能します。Zoo私の間違いは、サブクラスの明示的なリストに依存したくなかったAnimalため、この登録方法を導入したことです。回答で指摘されているように、自己登録メカニズムと依存性注入を組み合わせても、不必要な複雑さがなければ機能しません。
答えは、 がのコレクションにZoo依存していることを宣言し、Spring がオートワイヤリングによってコレクションにデータを入力できるようにすることです。Animal
したがって、コレクション メンバーのハード リストはなく、Spring によって検出されますが、依存関係が正しく表現されているため、@PostConstruct必要な順序でメソッドが実行されます。
素晴らしい答えをありがとう。