依存性注入にSpringを使用しているシステムがあります。注釈ベースの自動配線を使用します。Bean はコンポーネント スキャンによって検出されます。つまり、コンテキスト XML には次のものが含まれます。
<context:component-scan base-package="org.example"/>
私の問題を説明するために、以下のうなずく例を作成しました。
Zoo
オブジェクトのコンテナである がありAnimal
ます。の開発者は、開発中にどのオブジェクトが含まれるZoo
かを知りません。Spring によってインスタンス化された具体的なオブジェクトのセットはコンパイル時に認識されますが、さまざまなビルド プロファイルが存在するため、さまざまな のセットが生成され、これらの状況下でのコードを変更してはなりません。Animal
Zoo
Animal
Animal
Zoo
の目的は、特定の に明示的に依存する必要なく、実行時にZoo
システムの他の部分 (ここでは として示されているZooPatron
) が一連のオブジェクトにアクセスできるようにすることです。Animal
Animal
実際、具象Animal
クラスはすべて、さまざまな Maven アーティファクトによって提供されます。これらの具象を含むさまざまなアーティファクトに依存するだけでプロジェクトのディストリビューションを組み立て、Animal
コンパイル時にすべてを正しく自動配線できるようにしたいと考えています。
Animal
私は、個人が に依存するようにして、 duringZoo
で登録メソッドを呼び出せるようにすることで、この問題を解決しようとしました (失敗しました) 。これにより、 が の明示的なリストに明示的に依存することが回避されます。Zoo
@PostConstruct
Zoo
Animal
このアプローチの問題点は、すべての が登録されているZoo
場合にのみ、顧客が対話を希望することです。コンパイル時に既知の s の有限セットがあり、登録はすべてライフサイクルの Spring ワイヤリング フェーズ中に行われるため、サブスクリプション モデルは不要である必要があります (つまり、実行時にs に s を追加したくありません)。)。Animal
Animal
Animal
Zoo
残念ながら、 のすべての顧客はZoo
単に に依存していますZoo
。これは、Animal
が と持っている関係とまったく同じZoo
です。したがって、 と のメソッド@PostConstruct
は任意の順序で呼び出されます。これは、以下のコード例で示されています。 on が呼び出された時点で、 が登録されていません。数ミリ秒後にすべて登録されます。Animal
ZooPatron
@PostConstruct
ZooPatron
Animal
したがって、ここには 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
必要な順序でメソッドが実行されます。
素晴らしい答えをありがとう。