insertar entitymanagerfactory datos consultas con java spring spring-annotations

java - entitymanagerfactory - ¿Es posible garantizar el orden en que se invocan los métodos @PostConstruct?



entitymanager jpa (5)

En su lugar, podría tener el Juego de Animales @Inject ed en el zoológico.

@Component public class Zoo { @Inject private Set<Animal> animals = new HashSet<Animal>(); // ... }

Luego, se debe @PostConstruct de Zoo una vez que se hayan inyectado todos los animales. El único problema es que debe haber al menos un Animal en el sistema, pero no parece que eso deba ser un problema.

Tengo un sistema que utiliza Spring para inyección de dependencia. Yo uso autowiring basado en anotaciones. Los beans se descubren mediante la exploración de componentes, es decir, mi contexto XML contiene esto:

<context:component-scan base-package="org.example"/>

He creado un ejemplo de noddy a continuación para ilustrar mi problema.

Hay un Zoo que es un contenedor para objetos de Animal . El desarrollador de Zoo no sabe qué objetos de Animal se contendrán mientras él está desarrollando Zoo ; el conjunto de objetos de Animal de hormigón ejemplificados por Spring se conoce 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 de Zoo no debe cambiar en estas circunstancias.

El propósito de Zoo es permitir que otras partes del sistema (ilustradas aquí como ZooPatron ) accedan al conjunto de objetos Animal en tiempo de ejecución, sin necesidad de depender explícitamente de ciertos Animal .

En realidad, las clases concretas de Animal serán aportadas por varios artefactos de Maven. Quiero poder ensamblar una distribución de mi proyecto simplemente dependiendo de los diversos artefactos que contienen estos Animal hormigón, y tener todo correctamente instalado en el momento de la compilación.

He intentado resolver este problema (sin éxito) haciendo que los Animal individuales dependan del Zoo , para que puedan llamar a un método de registro en el Zoo durante @PostConstruct . Esto evita que el Zoo dependa explícitamente de una lista explícita de Animal .

El problema con este enfoque es que los clientes de Zoo desean interactuar con él solo cuando todos los Animal hayan registrado . Hay un conjunto finito de Animal s que se conoce en tiempo de compilación, y el registro se realiza durante la fase de cableado de Spring de mi ciclo de vida, por lo que un modelo de suscripción no es necesario (es decir, no deseo agregar Animal s al Zoo en tiempo de ejecución).

Desafortunadamente, todos los clientes de Zoo simplemente dependen de Zoo . Esta es exactamente la misma relación que los Animal tienen con Zoo . Por lo tanto, los métodos @PostConstruct de Animal s y ZooPatron se llaman en una secuencia arbitraria. Esto se ilustra con el código de ejemplo a continuación: en el momento en que se invoca ZooPatron en ZooPatron , no se ha registrado ningún Animal , es decir, unos milisegundos más tarde cuando todos se registran.

Entonces hay dos tipos de dependencia aquí, que estoy luchando por expresar en la primavera. Los clientes de Zoo solo quieren usarlo una vez que todos los Animal 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 puede garantizar el orden de @PostConstruct llamadas de @PostConstruct sin salir "fuera" de Spring o modificar su comportamiento.

El problema real aquí no era que quisiera secuenciar las invocaciones de @PostConstruct , eso era simplemente un síntoma de que las dependencias se expresaban incorrectamente.

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 subclases de Animal , por lo que introduje este método de registro. Como se señala en las respuestas, la combinación de un mecanismo de registro automático con la inyección de dependencia nunca funcionará sin una complejidad innecesaria.

La respuesta es declarar que Zoo depende de una colección de Animal , luego permitir que Spring complete la colección a través del cableado automático.

Por lo tanto, no hay una lista @PostConstruct de miembros de la colección, los descubrió Spring, pero las dependencias se expresan correctamente y, por lo tanto, los métodos @PostConstruct en la secuencia que quiero.

Gracias por las excelentes respuestas.


La mejor manera, IMO, es evitar hacer demasiado trabajo durante la construcción del gráfico de objetos (al igual que en Java, evita hacer demasiado trabajo en el constructor) y evitar llamar a los métodos desde dependencias cuando no esté seguro están completamente inicializados todavía.

Si simplemente elimina la anotación @PostConstruct del método Test#init() , y simplemente la invoca desde su método principal, una vez que se haya creado el contexto, ya no tendrá este problema.


Me parece que en tu caso hay una dependencia entre el objeto Zoo y todos tus tipos de animales. Si diseña su objeto Zoo para reflejar esta dependencia, el problema se resuelve. Por ejemplo podrías hacer:

<bean id="zoo" class="Zoo"> <property name="animals"> <list> <ref bean="Monkey" /> <ref bean="Tiger" /> <ref bean="Lion" /> </list> </property> </bean>

En lugar de utilizar el método de registro.


No creo que haya una forma de asegurar el orden de @PostConstruct sin introducir dependencias.

Creo que estás buscando problemas para mezclar la inyección o el registro automático. Hasta cierto punto, el orden de llamadas de @PostConstruct no debería importar; si lo hace, puede que no sea la herramienta adecuada para el trabajo.

Un par de ideas para tu ejemplo.

  • intente tener un @Autowired en Zoo # animales: no es necesario que los animales se registren a sí mismos, también el zoológico es consciente de los animales pero no al revés, lo que se siente más limpio
  • mantén el registro, pero deja que los actores externos hagan el registro (alguien está colocando a los animales en el zoológico, ¿no? No están apareciendo en la entrada por sí mismos)
  • Si necesita insertar nuevos animales en cualquier momento, pero no desea la inserción manual, haga un acceso más dinámico en el zoológico: no almacene la lista de animales, sino use el contexto Spring para obtener todas las instancias existentes de la interfaz.

No creo que haya una respuesta "correcta", todo depende de su caso de uso.


Replantea tu problema para que no se base en el orden de invocación.