2010-08-14 32 views

Respuesta

30

Como han dicho las otras respuestas, Spring solo se encarga de ello, creando los granos e inyectándolos 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 los archivos de su 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 código para verificar que las propiedades importantes se hayan establecido realmente).

11

Simplemente lo hace. Crea instancias a y b, e inyecta cada una en la otra (usando sus métodos setter).

¿Cuál es el problema?

+2

¿Funcionará con la inyección de constructor. – javaguy

+7

@javaguy: No, no lo hará. – skaffman

+0

@skaffman única forma con después de las propiedadesConfigurar el uso del método adecuado? – gstackoverflow

61

El Spring reference manual explica cómo se resuelven las dependencias circulares. Primero se crean instancias de los beans, 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 a continuación ha tenido este fichero 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> 

se podría ver la siguiente salida cuando creando 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 no cuando se inyecta en ab, a es inicializado todavía completamente.

+19

Es por eso que Spring requiere un constructor sin argumentos ;-) –

+10

¡No si usa argumentos de constructor en sus definiciones de bean! (Pero en ese caso no puede tener una dependencia circular.) –

+1

@Richard Fearn ¿Su publicación trata sobre la explicación del problema en lugar de proporcionar la solución? – gstackoverflow

6

Desde el Spring Reference:

por lo general puede confiar en primavera para hacer lo correcto. Detecta problemas de configuración, como referencias a beans no existentes y dependencias circulares, en el contenedor load-time. Primavera establece las propiedades y resuelva las dependencias tan tarde como sea posible , cuando el grano es en realidad creado.

4

El contenedor Spring puede resolver dependencias circulares basadas en Setter pero ofrece una excepción de tiempo de ejecución BeanCurrentlyInCreationException en el 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. Para por ejemplo., Si Bean A tiene una dependencia sobre el Bean B y Bean B sobre el Bean C, el contenedor inicializa totalmente C antes de inyectarlo a B y una vez B está totalmente inicializado Se inyecta a A. Sin embargo, en caso de dependencia circular, uno de los granos se inyecta a la otra antes de que esté completamente inicializado.

14

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. Estábamos recibiendo 12000+ FactoryBeanNotInitializedException.

lo que hice fue poner un breakpoint 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 hace destroySingleton(beanName) Imprimí la excepción con condicional código de punto de ruptura:

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

Al parecer esto sucede cuando FactoryBean s participan en un gráfico de dependencia cíclica. Lo solucionamos implementando ApplicationContextAware y InitializingBean e inyectando manualmente los granos.

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 reduce el tiempo de arranque a alrededor de 15 segundos.

Así que no siempre suponga 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.

+2

Interesante recomendación. Mi recomendación contraria sería solo hacer eso si * sospechas * que las referencias circulares están causando un problema de rendimiento. (Sería una pena romper algo que no necesita ser roto tratando de solucionar un problema que no necesita ser reparado). –

+2

Es una pendiente resbaladiza hasta el infierno de mantenimiento para permitir dependencias circulares, rediseñando su arquitectura de circular las dependencias pueden ser realmente engañosas, como en nuestro caso. Lo que más o menos significaba para nosotros era que obtuvimos el doble de conexiones a la base de datos durante el inicio, ya que sessionfactory estaba involucrado en la dependencia circular. En otros escenarios, cosas mucho más desastrosas podrían haber ocurrido debido a que el bean fue instanciado más de 12000 veces. Claro que deberías escribir tus granos para que apoyen la destrucción de ellos, pero ¿por qué permitir este comportamiento en primer lugar? – jontejj

+0

@jontejj, usted merece una galleta – serprime

2

Say A depende de B, entonces la primavera primera instancia será propiedades A, entonces B, entonces B establecidos para, a continuación, el conjunto B en A.

Pero lo que si B también depende de A?

Según tengo entendido: Spring acaba de encontrar que A ha sido construido (constructor ejecutado), pero no completamente inicializado (no todas las inyecciones hechas), bueno, pensó, está bien, es tolerable que A no esté completamente inicializado, solo Por ahora, configure las instancias A no iniciadas por completo en B. 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 otras a través del modo constructor-arg.

3

Se explica claramente here. Gracias a Eugen Paraschiv.

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

0

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

0

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 n tanto las definiciones de frijol, pero las inyecciones constructor tanto en tiro 'BeanCurrentlyInCreationException'

Consulte doc primavera en "https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource"

0

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': Bean solicitado se encuentra actualmente en la creación: ¿Hay una referencia circular no resuelta?

Solu ció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 }; 
} 
Cuestiones relacionadas