Tengo un sistema que usa Spring para inyección de dependencia. Yo uso el autoenvío basado en anotaciones. Los granos se descubrieron en los escaneos componente, es decir, mi contexto XML contiene esto:¿Es posible garantizar el orden en que se invocan los métodos de @PostConstruct?
<context:component-scan base-package="org.example"/>
He creado un ejemplo Noddy a continuación para ilustrar mi problema.
Hay un Zoo
que es un contenedor para objetos Animal
. El desarrollador de Zoo
no sabe qué objetos Animal
estarán contenidos mientras desarrolla Zoo
; el conjunto de objetos concretos Animal
instanciados por Spring es conocido en tiempo de compilación, pero hay varios perfiles de compilación que dan como resultado varios conjuntos de Animal
s, y el código para Zoo
no debe cambiar en estas circunstancias.
El propósito de Zoo
es para permitir que otras partes del sistema (ilustrado aquí como ZooPatron
) para acceder al conjunto de Animal
objetos en tiempo de ejecución, sin necesidad de depender explícitamente en ciertos Animal
s.
En realidad, las clases de hormigón Animal
serán contribuidas por varios artefactos de Maven. Quiero ser capaz de armar una distribución de mi proyecto simplemente dependiendo de los diversos artefactos que contienen estos concretos Animal
s, y hacer que todo se autoenlace correctamente en tiempo de compilación.
He intentado resolver este problema (sin éxito) por tener los individuales Animal
s dependen de la Zoo
, con el fin de que puedan llamar a un método de registro en el Zoo
durante @PostConstruct
. Esto evita el Zoo
dependiendo explícitamente en una lista explícita de Animal
s.
El problema con este enfoque es que los clientes de Zoo
desean interactuar con él sólo cuando todos los Animal
s han registrado. Hay un conjunto finito de Animal
s que se conoce en tiempo de compilación, y el registro ocurre durante la fase de cableado de Spring de mi ciclo de vida, por lo que un modelo de suscripción debería ser innecesario (es decir, no deseo agregar Animal
s al Zoo
en tiempo de ejecución).
Lamentablemente, todos los clientes de Zoo
simplemente dependen de Zoo
. Esta es exactamente la misma relación que el Animal
s con Zoo
. Por lo tanto, los métodos @PostConstruct
de Animal
sy ZooPatron
se invocan en una secuencia arbitraria. Esto se ilustra con el código de ejemplo siguiente: en el momento en que se invoca @PostConstruct
en ZooPatron
, no se ha registrado Animal
, algunos milisegundos más tarde cuando se registran todos.
Así que hay dos tipos de dependencia aquí, que estoy luchando para expresar en primavera. Los clientes de Zoo
solo quieren usarlo una vez que todos los Animal
s estén en él. (quizás "Ark" hubiera sido un mejor ejemplo ...)
Mi pregunta es básicamente: ¿cuál es la mejor manera de resolver este problema?
@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");
}
}
Salida:
There are 0 different animals.
There are 3 different animals.
There are 4 different animals.
There are 4 different animals.
... etc
Explicación de la solución aceptada
Básicamente la respuesta es: no, no se puede garantizar el orden de @PostConstruct
llamadas sin bien va Primavera "fuera" o modificar su comportamiento .
El verdadero problema aquí era no que quería para secuenciar los @PostConstruct
invocaciones, que no era más que un síntoma de las dependencias que se expresa de forma incorrecta.
Si los consumidores de Zoo
dependen de él, y Zoo
a su vez depende de Animal
s, todo funciona correctamente. Mi error fue que no quería que Zoo
dependiera de una lista explícita de las subclases Animal
, y por lo tanto introduje este método de registro. Como se señala en las respuestas, mezclar un mecanismo de autorregistro con inyección de dependencia nunca funcionará sin una complejidad innecesaria.
La respuesta es declarar que Zoo
depende de una de recogida de Animal
s, y luego permitir la primavera para poblar la colección a través de auto-cableado.
Por lo tanto, no hay una lista difícil de miembros de la colección, sino Spring los descubre, pero las dependencias se expresan correctamente y, por lo tanto, los métodos @PostConstruct
ocurren en la secuencia que deseo.
Gracias por las excelentes respuestas.
Ok, solo lo comprobé muy rápido ... pero creo que Jirafa, mono, león y tiget también deberían tener un @PostConstuct. ¿Y por qué la estática? – Cygnusx1
Esos animales concretos no necesitan PostConstruct, el método PostConstruct de su padre abstracto es invocado correctamente por Spring. –