2010-08-14 15 views

risposta

30

Come le altre risposte hanno detto, Spring si prende solo cura di esso, creando i fagioli e iniettandoli secondo necessità.

Una delle conseguenze è che l'impostazione del bean injection/property potrebbe verificarsi in un ordine diverso da quello che i file di cablaggio XML sembrano implicare. Quindi devi stare attento che i tuoi setter non facciano un'inizializzazione che si basa su altri setter già chiamati. Il modo per risolvere questo problema è dichiarare i bean come l'implementazione dell'interfaccia InitializingBean. Ciò richiede di implementare il metodo afterPropertiesSet() e qui si esegue l'inizializzazione critica. (Includo anche il codice per verificare che siano state effettivamente impostate le proprietà importanti.)

11

Lo fa e basta. Crea un'istanza di a e b e inietta ciascuno nell'altro (utilizzando i loro metodi di settaggio).

Qual è il problema?

+2

Funzionerà con l'iniezione del costruttore. – javaguy

+7

@javaguy: No, non lo farà. – skaffman

+0

@skaffman solo modo dopo l'utilizzo del metodo propertiesSet appropriato? – gstackoverflow

61

Il Spring reference manual spiega come vengono risolte le dipendenze circolari. I fagioli vengono prima istanziati, quindi iniettati l'uno nell'altro.

Considerate questa classe:

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; 
    } 

} 

E una classe simile 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; 
    } 

} 

Se allora avessi questo file di configurazione:

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

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

si dovrebbe vedere il seguente output quando creando un contesto usando questa configurazione:

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

noti che non quando a viene iniettata b, a è ancora completamente inizializzato.

+19

Ecco perché Spring richiede un costruttore senza argomenti ;-) –

+10

Non se si usano gli argomenti del costruttore nelle definizioni dei bean! (Ma in tal caso non è possibile avere una dipendenza circolare.) –

+1

@Richard Fearn Il post riguarda la spiegazione del problema piuttosto che la fornitura della soluzione? – gstackoverflow

6

Dal Spring Reference:

si può generalmente fiducia primavera per fare la cosa giusta. Rileva i problemi di configurazione , ad esempio i riferimenti a bean inesistenti e le dipendenze circolari , al momento del caricamento del contenitore . Le proprietà dei set di molle e risolvono le dipendenze fino a possibili, quando il bean è in realtà creato.

4

Il contenitore Spring è in grado di risolvere le dipendenze circolari basate su Setter ma fornisce un'eccezione di runtime BeanCurrentlyInCreationException in caso di dipendenze circolari basate su Constructor. In caso di dipendenza circolare basata su Setter, il contenitore IOC la gestisce in modo diverso da uno scenario tipico in cui configurerebbe completamente il bean collaborativo prima di iniettarlo. Ad esempio, se il bean A ha una dipendenza da Bean B e Bean B sul bean C, il contenitore inizializza completamente C prima di iniettarlo in B e una volta che B è inizializzato completamente viene iniettato in A. Ma in caso di dipendenza circolare, uno dei fagioli viene iniettato all'altro prima che sia completamente inizializzato.

14

Nella base di codice con cui sto lavorando (1 milione + righe di codice) abbiamo riscontrato un problema con tempi di avvio lunghi, circa 60 secondi. Abbiamo ricevuto 12000+ FactoryBeanNotInitializedException.

Quello che ho fatto è stato impostato un punto di interruzione condizionale in 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; 
} 

in cui lo fa destroySingleton(beanName) ho stampato l'eccezione con il codice di breakpoint condizionale:

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

A quanto pare questo accade quando FactoryBean s sono coinvolti in un grafico di dipendenza ciclica. Lo abbiamo risolto implementando ApplicationContextAware e InitializingBean e iniettando manualmente i fagioli.

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(); 
    } 
} 

Questo riduce il tempo di avvio a circa 15 secondi.

Quindi non sempre supponiamo che la primavera possa essere utile a risolvere questi riferimenti per te.

Per questo motivo, consigliamo di disabilitare la risoluzione di dipendenza ciclica con AbstractRefreshableApplicationContext#setAllowCircularReferences(false) per evitare molti problemi futuri.

+2

Interessante? raccomandazione. La mia contro raccomandazione sarebbe di farlo solo se * sospetti * che i riferimenti circolari stiano causando un problema di prestazioni. (Sarebbe un peccato rompere qualcosa che non ha bisogno di essere rotto cercando di risolvere un problema che non ha bisogno di essere riparato.) –

+2

È una pendenza scivolosa fino all'inferno di manutenzione per consentire dipendenze circolari, ridisegnando la tua architettura da circolare le dipendenze possono essere davvero complicate, come nel nostro caso. Ciò che per noi significava approssimativamente era che ottenevamo il doppio delle connessioni al database durante l'avvio in quanto la sessionfactory era coinvolta nella dipendenza circolare. In altri scenari si sarebbero potute verificare cose molto più disastrose a causa del fatto che il bean veniva istanziato più di 12000 volte. Certo, dovresti scrivere i tuoi fagioli in modo che supportino la loro distruzione, ma perché consentire questo comportamento in primo luogo? – jontejj

+0

@jontejj, ti meriti un cookie – serprime

2

Say A dipende da B, allora primavera in primo luogo un'istanza di A, poi B, poi impostare le proprietà per la B, quindi impostare B in A.

Ma cosa succede se B dipende anche da A?

mia comprensione è: Primavera appena scoperto che A è stato costruito (costruttore eseguito), ma non completamente inizializzato (non tutte le iniezioni fatte), beh, è ​​pensato, va bene, è tollerabile che A non è completamente inizializzato, basta per ora imposta queste istanze A non completamente inizializzate in B. Dopo che B è stato completamente inizializzato, è stato impostato su A e, infine, A è stato avviato completamente ora.

In altre parole, espone semplicemente A a B in anticipo.

Per le dipendenze tramite il costruttore, Sprint lancia appena BeanCurrentlyInCreationException, per risolvere questa eccezione, impostare lazy-init su true per il bean che dipende dagli altri tramite il modo costruttore-arg.

3

È chiaramente spiegato here. Grazie a Eugen Paraschiv.

La dipendenza circolare è un odore di progettazione, oppure risolverlo o utilizzare @Lazy per la dipendenza che causa il problema di risolvere il problema.

0

Utilizzando Iniezione o Iniezione campo o usando @Lazy per la dipendenza.

0

Se due bean sono dipendenti l'uno dall'altro, non è consigliabile utilizzare l'iniezione del costruttore in entrambe le definizioni dei bean. Invece dobbiamo usare l'iniezione setter in uno qualsiasi dei fagioli.(Naturalmente possiamo utilizzare l'iniezione setter n entrambe le definizioni di fagioli, ma iniezioni costruttore sia in tiro 'BeanCurrentlyInCreationException'

consultare doc Primavera al "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 }; 
} 

// Causato da: org.springframework.beans.factory.BeanCurrentlyInCreationException: Errore nella creazione di bean con nome "A": bean richiesto è attualmente in fase di creazione: esiste un riferimento circolare irrisolvibile?

Solu zione 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 
} 

Soluzione 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 }; 
} 
Problemi correlati