tutorial sts framework java spring

java - sts - Dependencia circular en primavera



spring wikipedia (13)

Al usar Setter Injection o Field Injection o al usar @Lazy para la dependencia.

¿Cómo resuelve la primavera esto? El frijol A depende del frijol b, y el frijol b del frijol a.


Como han dicho las otras respuestas, Spring solo se encarga de crear las semillas e inyectarlas según sea necesario.

Una de las consecuencias es que la configuración de inyección / propiedad de frijol puede ocurrir en un orden diferente al que parecen implicar sus archivos de cableado XML. Por lo tanto, debe tener cuidado de que sus instaladores de propiedades no realicen la inicialización que depende de otros instaladores que ya hayan sido llamados. La forma de lidiar con esto es declarar que los beans implementan la interfaz InitializingBean . Esto requiere que implemente el método afterPropertiesSet() , y aquí es donde realiza la inicialización crítica. (También incluyo el código para verificar que las propiedades importantes realmente se hayan establecido).


De la referencia de primavera :

Generalmente puede confiar en Spring para hacer lo correcto. Detecta problemas de configuración, como referencias a beans no existentes y dependencias circulares, en el tiempo de carga del contenedor. Spring establece las propiedades y resuelve las dependencias lo más tarde posible, cuando realmente se crea el bean.


Decir A depende de B, luego Spring creará primero una instancia A, luego B, luego establecerá las propiedades para B, luego establecerá B en A.

Pero, ¿y si B también depende de A?

Mi comprensión es: Spring acaba de descubrir que A se ha construido (constructor ejecutado), pero no se ha inicializado por completo (no se han aplicado todas las inyecciones), bueno, pensó, está bien, es tolerable que A no esté completamente inicializado, simplemente configure esto no- instancias A completamente inicializadas en B por ahora. Después de que B se inicializó por completo, se estableció en A, y finalmente, A se inició completamente ahora.

En otras palabras, solo expone A a B por adelantado.

Para las dependencias a través del constructor, Sprint acaba de lanzar BeanCurrentlyInCreationException, para resolver esta excepción, establezca lazy-init en true para el bean que depende de otros a través del modo constructor-arg.


El manual de referencia de Spring explica cómo se resuelven las dependencias circulares. Primero se crean instancias de los beans, luego se inyectan entre sí.

Considera esta clase:

package mypackage; public class A { public A() { System.out.println("Creating instance of A"); } private B b; public void setB(B b) { System.out.println("Setting property b of A instance"); this.b = b; } }

Y una clase similar B :

package mypackage; public class B { public B() { System.out.println("Creating instance of B"); } private A a; public void setA(A a) { System.out.println("Setting property a of B instance"); this.a = a; } }

Si luego tuvieras este archivo de configuración:

<bean id="a" class="mypackage.A"> <property name="b" ref="b" /> </bean> <bean id="b" class="mypackage.B"> <property name="a" ref="a" /> </bean>

Vería el siguiente resultado al crear un contexto usando esta configuración:

Creating instance of A Creating instance of B Setting property a of B instance Setting property b of A instance

Tenga en cuenta que cuando a se inyecta en b , a aún no se ha inicializado por completo.


El contenedor Spring es capaz de resolver dependencias circulares basadas en Setter pero ofrece una excepción de tiempo de ejecución BeanCurrentlyInCreationException en caso de dependencias circulares basadas en Constructor. En caso de dependencia circular basada en Setter, el contenedor IOC lo maneja de forma diferente a un escenario típico en el que configuraría completamente el bean colaborador antes de inyectarlo. Por ejemplo, si Bean A tiene una dependencia en Bean B y Bean B en Bean C, el contenedor inicializa completamente C antes de inyectarlo a B y una vez que B está completamente inicializado se inyecta a A. Pero en caso de dependencia circular, uno de los granos se inyecta a la otra antes de que esté completamente inicializado.


En la base de código con la que estoy trabajando (1 millón de líneas de código), tuvimos un problema con los tiempos de inicio largos, de alrededor de 60 segundos. Obtenemos 12,000+ FactoryBeanNotInitializedException .

Lo que hice fue establecer un punto de interrupción condicional en AbstractBeanFactory#doGetBean

catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; }

donde destroySingleton(beanName) la excepción con código de punto de interrupción condicional:

System.out.println(ex); return false;

Aparentemente esto sucede cuando los FactoryBean están involucrados en un gráfico de dependencia cíclica. Lo solucionamos implementando ApplicationContextAware e InitializingBean e inyectando manualmente los beans.

import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class A implements ApplicationContextAware, InitializingBean{ private B cyclicDepenency; private ApplicationContext ctx; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ctx = applicationContext; } @Override public void afterPropertiesSet() throws Exception { cyclicDepenency = ctx.getBean(B.class); } public void useCyclicDependency() { cyclicDepenency.doSomething(); } }

Esto redujo el tiempo de inicio a alrededor de 15 segundos.

Por lo tanto, no siempre asuma que la primavera puede ser buena para resolver estas referencias por usted.

Por esta razón, recomendaría deshabilitar la resolución de dependencia cíclica con AbstractRefreshableApplicationContext#setAllowCircularReferences(false) para evitar muchos problemas futuros.


Está claramente explicado baeldung.com/circular-dependencies-in-spring . Gracias a Eugen Paraschiv.

La dependencia circular es un olor de diseño, ya sea solucionarlo o usar @Lazy para la dependencia que causa problemas para solucionarlo.


La inyección de constructor falla cuando existe una dependencia circular entre los granos de primavera. Entonces, en este caso, la inyección de Setter ayuda a resolver el problema.

Básicamente, la inyección de Constructor es útil para dependencias obligatorias, para dependencias opcionales es mejor utilizar la inyección Setter porque podemos hacer la reinyección.


Problema ->

Class A { private final B b; // must initialize in ctor/instance block public A(B b) { this.b = b }; } Class B { private final A a; // must initialize in ctor/instance block public B(A a) { this.a = a }; }

// Causado por: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error al crear bean con el nombre ''A'': El bean solicitado se encuentra actualmente en la creación: ¿Hay una referencia circular no resuelta?

Solución 1 ->

Class A { private B b; public A( ) { }; //getter-setter for B b } Class B { private A a; public B( ) { }; //getter-setter for A a }

Solución 2 ->

Class A { private final B b; // must initialize in ctor/instance block public A(@Lazy B b) { this.b = b }; } Class B { private final A a; // must initialize in ctor/instance block public B(A a) { this.a = a }; }


Si dos beans dependen el uno del otro, no deberíamos usar la inyección de Constructor en ambas definiciones de bean. En cambio, tenemos que usar la inyección setter en cualquiera de los beans. (Por supuesto, podemos usar la inyección de setter en ambas definiciones de bean, pero las inyecciones de constructor en ambos lanzamos ''BeanCurrentlyInCreationException''

Consulte el documento de Spring en " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource "



Simplemente lo hace. Instancia a y b , e inyecta cada uno en el otro (utilizando sus métodos setter).

¿Cuál es el problema?