Dependencia circular en primavera

Respuestas:

42

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 InitializingBeaninterfaz. 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).

Stephen C
fuente
76

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 ase inyecta b, aaún no se ha inicializado por completo.

Richard Fearn
fuente
26
Es por eso que Spring requiere un constructor sin argumentos ;-)
Chris Thompson
15
¡No si usa argumentos de constructor en sus definiciones de frijol! (Pero en ese caso no puede tener una dependencia circular.)
Richard Fearn
1
@Richard Fearn ¿Tu publicación acerca de la explicación del problema en lugar de proporcionar una solución?
gstackoverflow
4
Si intenta utilizar la inyección del constructor, el mensaje de error esorg.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
X. Wo Satuk
19

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.

jontejj
fuente
3
Interesante recomendación. Mi contra recomendación sería solo hacer eso si sospecha que las referencias circulares están causando un problema de rendimiento. (Sería una lástima romper algo que no necesitaba romperse al tratar de solucionar un problema que no necesitaba ser arreglado.)
Stephen C
2
Es una pendiente resbaladiza hacia el infierno del mantenimiento para permitir dependencias circulares, rediseñar su arquitectura a partir de dependencias circulares puede ser realmente complicado, por así decirlo en nuestro caso. Lo que significó más o menos para nosotros fue que obtuvimos el doble de conexiones de base de datos durante el inicio que las de sessionfactory involucradas en la dependencia circular. En otros escenarios, podrían haber ocurrido cosas mucho más desastrosas debido a que el bean se instanciara más de 12000 veces. Seguro que deberías escribir tus beans para que soporten su destrucción, pero ¿por qué permitir este comportamiento en primer lugar?
jontejj
@jontejj, te mereces una galleta
serprime
14

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 };
}
Akshay N. Shelke
fuente
12

Simplemente lo hace. Instancia ay b, e inyecta cada uno en el otro (usando sus métodos de establecimiento).

¿Cuál es el problema?

skaffman
fuente
9
@javaguy: No, no lo hará.
skaffman
@skaffman es la única forma después de las propiedades ¿Establecer el uso correcto del método?
gstackoverflow
6

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.

Earldouglas
fuente
6

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.

Saurav S.
fuente
5

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.

Silencio
fuente
simple y una de las mejores explicaciones.
Sritam Jagadev
5

Aquí se explica claramente . Gracias a Eugen Paraschiv.

La dependencia circular es un olor de diseño, ya sea arréglelo o use @Lazy para la dependencia que causa el problema para solucionarlo.

quién soy
fuente
3

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.

Premraj
fuente
0

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 "

Srikant M
fuente