У меня есть система, которая использует Spring для внедрения зависимостей.Я использую аннотации на основе автопроводки.Компоненты обнаруживаются при сканировании компонентов, то есть мой контекстный XML содержит следующее:
<context:component-scan base-package="org.example"/>
Я создал приведенный ниже пример, чтобы проиллюстрировать мою проблему.
Существует Zoo
, которыйконтейнер для Animal
объектов.Разработчик Zoo
не знает, какие Animal
объекты будут содержаться во время разработки Zoo
;набор конкретных Animal
объектов, созданных в Spring, известен во время компиляции, но существуют различные профили сборки, приводящие к различным наборам Animal
s, и код для Zoo
не должен изменяться при этих обстоятельствах.
Целью Zoo
является предоставление другим частям системы (обозначенным здесь как ZooPatron
) доступа к набору объектов Animal
во время выполнения без необходимости явной зависимости от определенных Animal
s.
На самом деле конкретные классы Animal
будут предоставляться различными артефактами Maven.Я хочу иметь возможность собрать дистрибутив своего проекта, просто полагаясь на различные артефакты, содержащие эти конкретные Animal
s, и правильно все автоматически подключать во время компиляции.
Я попытался решить эту проблему(безуспешно), когда отдельные Animal
зависят от Zoo
, чтобы они могли вызвать метод регистрации на Zoo
в течение @PostConstruct
.Это позволяет избежать зависимости Zoo
, явно зависящей от явного списка Animal
с.
. Проблема с этим подходом заключается в том, что клиенты Zoo
хотят взаимодействовать с ним только тогда, когда все Animal
зарегистрировалось .Существует конечный набор Animal
с, который известен во время компиляции, и регистрация происходит во время фазы подключения Spring моего жизненного цикла, поэтому модель подписки должна быть ненужной (то есть я не хочу добавлять Animal
s до Zoo
во время выполнения).
К сожалению, все клиенты Zoo
просто зависят от Zoo
.Это точно такое же отношение, как у Animal
с Zoo
.Поэтому методы @PostConstruct
для Animal
s и ZooPatron
вызываются в произвольной последовательности.Это иллюстрируется приведенным ниже примером кода - в момент, когда @PostConstruct
вызывается на ZooPatron
, Animal
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
зависят от него, а Zoo
в свою очередь зависит от Animal
s, все работает правильно.Моя ошибка заключалась в том, что я не хотел, чтобы Zoo
зависел от явного списка Animal
подклассов, и поэтому ввел этот метод регистрации.Как указано в ответах, смешивание механизма саморегистрации с внедрением зависимостей никогда не будет работать без излишней сложности.
Ответ заключается в том, чтобы объявить, что Zoo
зависит от коллекции Animal
s, затем разрешите Spring заполнить коллекцию с помощью автоматического подключения.
Таким образом, нет жесткого списка членов коллекции, они обнаруживаются Spring, но зависимости правильно выражены и, следовательно, @PostConstruct
методов происходит в нужной мне последовательности.
Спасибо за отличные ответы.