¿Cómo resuelve Spring esto? El frijol A depende del frijol B y el frijol B del frijol A.
¿Cómo resuelve Spring esto? El frijol A depende del frijol B y el frijol B del frijol A.
Respuestas:
Como han dicho las otras respuestas, Spring solo se encarga de ello, crea los frijoles e inyecta según sea necesario.
Una de las consecuencias es que la configuración de la propiedad / inyección de bean puede ocurrir en un orden diferente al que parecen implicar los archivos de cableado XML. Por lo tanto, debe tener cuidado de que los establecedores de su propiedad no realicen una inicialización que dependa de que otros establecedores ya hayan sido llamados. La forma de lidiar con esto es declarar que los beans implementan la InitializingBean
interfaz. Esto requiere que implemente el afterPropertiesSet()
método, y aquí es donde realiza la inicialización crítica. (También incluyo código para verificar que realmente se hayan establecido propiedades importantes).
El manual de referencia de Spring explica cómo se resuelven las dependencias circulares. Los granos se instancian primero y luego se inyectan entre sí.
Considere 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 tenías 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 b
, a
aún no se ha inicializado por completo.
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
En el código base con el que estoy trabajando (más de 1 millón de líneas de código) tuvimos un problema con tiempos de inicio largos, alrededor de 60 segundos. Obtuvimos más de 12000 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 lo hace destroySingleton(beanName)
imprimí la excepción con el código de punto de interrupción condicional:
System.out.println(ex);
return false;
Aparentemente, esto sucede cuando los FactoryBean s 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 unos 15 segundos.
Por lo tanto, no asuma siempre que Spring puede 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.
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 el bean con el nombre 'A': El bean solicitado se está creando actualmente: ¿Hay una referencia circular irresoluble?
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 };
}
Simplemente lo hace. Instancia a
y b
, e inyecta cada uno en el otro (usando sus métodos de establecimiento).
¿Cuál es el problema?
De la referencia de primavera :
En general, puede confiar en que Spring hará lo correcto. Detecta problemas de configuración, como referencias a beans inexistentes y dependencias circulares, en el momento de la carga del contenedor. Spring establece las propiedades y resuelve las dependencias lo más tarde posible, cuando el bean se crea realmente.
El contenedor Spring es capaz de resolver dependencias circulares basadas en Setter pero da 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 manera 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 de Bean B y Bean B de Bean C, el contenedor inicializa completamente C antes de inyectarlo a B y una vez que B se inicializa completamente, se inyecta a A. Pero en caso de dependencia circular, uno de los beans se inyecta al otro antes de que se inicialice por completo.
Digamos que A depende de B, luego Spring primero instanciará A, luego B, luego establecerá 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 completamente (no se han realizado todas las inyecciones), bueno, pensó, está bien, es tolerable que A no esté completamente inicializado, simplemente establezca esto no- instancias A completamente inicializadas en B por ahora. Una vez que B se inicializó por completo, se estableció en A y, finalmente, A se inició completamente ahora.
En otras palabras, simplemente expone A a B de antemano.
Para las dependencias a través del constructor, Sprint simplemente lanza BeanCurrentlyInCreationException, para resolver esta excepción, establezca lazy-init en verdadero para el bean que depende de otros a través del constructor-arg.
Si generalmente usa constructor-injection y no desea cambiar a property-injection, entonces el método de búsqueda- inyección de Spring permitirá que un bean busque perezosamente al otro y, por lo tanto, solucione la dependencia cíclica. Vea aquí: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
La inyección del constructor falla cuando hay una dependencia circular entre los granos de primavera. Entonces, en este caso, la inyección de Setter ayuda a resolver el problema.
Básicamente, Constructor Injection es útil para dependencias obligatorias, para dependencias opcionales es mejor usar la inyección Setter porque podemos hacer reinyección.
Si dos beans son dependientes entre sí, entonces no deberíamos usar la inyección Constructor en ambas definiciones de bean. En su lugar, tenemos que usar la inyección de 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 ambas lanzan 'BeanCurrentlyInCreationException'
Consulte el documento de Spring en " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource "