Come risolve Spring questo: il fagiolo A dipende dal fagiolo B e il fagiolo B dal fagiolo A.
Come risolve Spring questo: il fagiolo A dipende dal fagiolo B e il fagiolo B dal fagiolo A.
Risposte:
Come hanno detto le altre risposte, Spring se ne prende cura, creando i fagioli e iniettandoli secondo necessità.
Una delle conseguenze è che l'iniezione di bean / l'impostazione della proprietà potrebbe avvenire in un ordine diverso da quello che i file di cablaggio XML sembrerebbero implicare. Quindi devi stare attento che i tuoi setter di proprietà non eseguano l'inizializzazione che si basa su altri setter già chiamati. Il modo per risolvere questo problema è dichiarare i bean come implementatori InitializingBean
dell'interfaccia. Ciò richiede l'implementazione del afterPropertiesSet()
metodo, ed è qui che si esegue l'inizializzazione critica. (Includo anche il codice per verificare che le proprietà importanti siano state effettivamente impostate.)
Il manuale di riferimento di Spring spiega come vengono risolte le dipendenze circolari. I fagioli vengono prima istanziati, quindi iniettati l'uno nell'altro.
Considera 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 poi avevi 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>
Vedrai il seguente output quando crei 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
Notare che quando a
viene iniettato b
, a
non è ancora completamente inizializzato.
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Nella base di codice con cui sto lavorando (1 milione + righe di codice) abbiamo avuto un problema con tempi di avvio lunghi, circa 60 secondi. Abbiamo ricevuto oltre 12000 FactoryBeanNotInitializedException .
Quello che ho fatto è stato impostare 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;
}
dove destroySingleton(beanName)
ho stampato l'eccezione con il codice del punto di interruzione condizionale:
System.out.println(ex);
return false;
Apparentemente questo accade quando i FactoryBean sono coinvolti in un grafico di dipendenza ciclico. Lo abbiamo risolto implementando ApplicationContextAware e InitializingBean e inserendo manualmente i bean.
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();
}
}
Ciò ha ridotto il tempo di avvio a circa 15 secondi.
Quindi non dare sempre per scontato che la primavera possa essere utile per risolvere questi riferimenti per te.
Per questo motivo consiglierei di disabilitare la risoluzione delle dipendenze cicliche con AbstractRefreshableApplicationContext # setAllowCircularReferences (false) per evitare molti problemi futuri.
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 };
}
// Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Errore durante la creazione del bean con nome "A": Il bean richiesto è attualmente in fase di creazione: C'è un riferimento circolare irrisolvibile?
Soluzione 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 };
}
Lo fa e basta. Istanzia a
e b
, e inietta l'uno nell'altro (usando i loro metodi setter).
Qual è il problema?
Dal riferimento di primavera :
In genere puoi fidarti di Spring per fare la cosa giusta. Rileva problemi di configurazione, come riferimenti a bean inesistenti e dipendenze circolari, al momento del caricamento del contenitore. Spring imposta le proprietà e risolve le dipendenze il più tardi possibile, quando il bean viene effettivamente creato.
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 sul costruttore. In caso di dipendenza circolare basata su Setter, il contenitore IOC lo gestisce in modo diverso da uno scenario tipico in cui configurerebbe completamente il bean collaborante prima di iniettarlo. Ad esempio, se Bean A ha una dipendenza da Bean B e Bean B da Bean C, il contenitore inizializza completamente C prima di iniettarlo in B e una volta che B è completamente inizializzato viene iniettato in A. Ma in caso di dipendenza circolare, uno dei fagioli viene iniettato all'altro prima che sia completamente inizializzato.
Diciamo che A dipende da B, quindi Spring creerà prima un'istanza di A, quindi B, quindi imposterà le proprietà per B, quindi imposterà B in A.
Ma cosa succede se B dipende anche da A?
La mia comprensione è: Spring ha appena scoperto che A è stato costruito (costruttore eseguito), ma non completamente inizializzato (non tutte le iniezioni fatte), beh, pensava, va bene, è tollerabile che A non sia completamente inizializzato, basta impostare questo non- istanze A completamente inizializzate in B per ora. Dopo che B è stato completamente inizializzato, è stato impostato in A e, infine, A è stato avviato completamente ora.
In altre parole, espone solo A a B in anticipo.
Per le dipendenze tramite il costruttore, Sprint lancia semplicemente BeanCurrentlyInCreationException, per risolvere questa eccezione, imposta lazy-init su true per il bean che dipende da altri tramite il modo costruttore-arg.
Se generalmente si utilizza l'iniezione del costruttore e non si desidera passare all'iniezione di proprietà, il metodo di ricerca di Spring -injection consentirà a un bean di cercare pigramente l'altro e quindi di aggirare la dipendenza ciclica. Vedi qui: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
L'iniezione del costruttore non riesce quando è presente una dipendenza circolare tra i bean primaverili. Quindi in questo caso noi Setter injection aiuta a risolvere il problema.
Fondamentalmente, l'iniezione del costruttore è utile per le dipendenze obbligatorie, per le dipendenze opzionali è meglio usare l'iniezione di Setter perché possiamo fare la re-iniezione.
Se due bean dipendono l'uno dall'altro, non dovremmo usare l'iniezione nel costruttore in entrambe le definizioni dei bean. Invece dobbiamo usare l'iniezione di setter in uno qualsiasi dei fagioli. (ovviamente possiamo usare l'iniezione setter in entrambe le definizioni dei bean, ma le iniezioni del costruttore in entrambe lanciano 'BeanCurrentlyInCreationException'
Fare riferimento al documento di primavera su " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource "