2012-07-18 9 views
15

Ho un grafico di fagioli Spring che si autowire l'un l'altro. Pesantemente rappresentazione semplificata:Spring crea più istanze di un singleton?

<context:annotation-config/> 
<bean class="Foo"/> 
<bean class="Bar"/> 
<bean class="Baz"/> 

... 

public class Foo { 
    @Autowired Bar bar; 
    @Autowired Baz baz; 
} 

public class Bar { 
    @Autowired Foo foo; 
} 

public class Baz { 
    @Autowired Foo foo; 
} 

Tutti questi fagioli non hanno ambito specificato che implicano sono single (Rendendole singletons espliciti non cambia nulla, ho provato).

Il problema è che, dopo l'istanza di un contesto singola applicazione, le istanze di Bar e Baz contengono diversi istanze di Foo. Come è potuto accadere?

Ho provato a creare public no constrs constructor per Foo e il debug ha confermato Foo viene creato più di una volta. La traccia di stack per tutte queste creazioni è here.

ho anche cercato di attivare la registrazione di debug per la primavera, e tra tutte le altre linee, ha ottenuto la seguente:

DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo' 
DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo' 
DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'Foo' 

ho capito che i miei fagioli sono riferimenti incrociati tra loro, ma mi si aspetterebbe framework Spring rispettare lo scope di singleton e inizializzare un bean singleton una volta e quindi autorizzarlo su chiunque lo desideri.

Il fatto interessante che se uso vecchia scuola private costruttore con public static Foo getInstance di accesso, questo funziona bene - senza eccezioni sono gettati durante l'installazione contesto.

FWIW, Sto usando Spring versione 3.0.5 (anche provato con 3.1.2, stessi risultati) con costruttore o.s.c.s.ClassPathXmlApplicationContext(String ...configLocations).

Posso facilmente convertire il mio codice per utilizzare l'inizializzatore statico ma voglio capire perché si comporterebbe in questo modo. è un insetto?

EDIT: Alcuni ulteriori indagini hanno dimostrato che

  • Dopo che il contesto di applicazione è inizializzato, tutte le richieste successive context.getBean(Foo.class)sempre restituiscono la stessa istanza di Foo.
  • Sostituire @Autowired con setter (circa 20 utilizzi di questo bean) risulta ancora più costruzioni di questo oggetto, ma tutte le dipendenze vengono iniettate con lo stesso riferimento.

Per quanto sopra suggerisco che si tratta di un bug di primavera relativo all'implementazione di @Autowired. Pubblicherò i forum della community Spring e pubblicheremo di nuovo qui se riuscirò a ottenere qualcosa di utile.

+0

Potrebbe essere ovvio, ma c'è solo 1 JVM in gioco? Dipendenze circolari? –

+0

Sì, questa è solo una JVM. Dipendenze circolari - sì, ma credo di averlo spiegato nel mio post. – mindas

+0

Vedo ma cosa succede se si dispone ad esempio di un'iniezione costruttore? In che modo Spring dovrebbe risolvere questo problema? –

risposta

11

Il contesto oi contesti secondari possono reintegrare gli stessi bean singleton se non si presta attenzione al contesto: annotazioni di scansione componenti (lì sono altre annotazioni di scansione del contesto Spring come quelle MVC e altre). Questo è un problema comune quando si utilizzano i servlet di primavera nelle applicazioni Web, vedere Why DispatcherServlet creates another application context?

Assicurarsi di non eseguire nuovamente la scansione dei componenti in contesti secondari oppure di eseguire la scansione solo di pacchetti/annotazioni specifici ed escludere detti pacchetti/annotazioni dalla radice scansione del componente di contesto.

+0

questi singletones caricheranno lo stesso caricatore di classi? – gstackoverflow

+0

Puoi fornire un esempio minimo in cui spring singleton caricherà due volte? – gstackoverflow

0

Provare a utilizzare l'iniezione setter invece del modo costruttore e vedere se funziona. Nel bean spring xml specificare Bean A ref to Bean B e viceversa.

+0

Ho aggiornato il mio post. Solo per ribadire: so come risolvere il problema, ma soprattutto cerco di capire ** perché ** questo accade. – mindas

0

La mia configurazione di Spring era come segue:

<context:annotation-config/> 

<bean class="Bar" /> 
<bean class="Foo" /> 
<bean class="Baz" /> 

Le classi sono identiche al tuo

test app come segue:

import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 

public class SpringTest { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF/spring/testctx.xml"); 

     Foo foo = ctx.getBean(Foo.class); 
     Baz baz = ctx.getBean(Baz.class); 
     Bar bar = ctx.getBean(Bar.class); 

     System.out.println(foo.equals(baz.foo)); 
     System.out.println(foo.equals(bar.foo)); 
     System.out.println(baz.equals(foo.baz)); 

     System.out.println(foo.baz.toString()); 
     System.out.println(baz.toString()); 
     System.out.println(foo.bar.toString()); 
     System.out.println(bar.toString()); 

    } 

} 

uscita dal test di app come segue:

true 
true 
true 
[email protected] 
[email protected] 
[email protected] 
[email protected] 

Utilizzando 3.0.6 funziona perfettamente bene (i fagioli singleton sono in effetti single). Potrebbe esserci qualcos'altro che non hai illustrato qui a rovinare la tua configurazione. Naturalmente, come nota a margine, l'uso del pacchetto predefinito può causare misteriosa magia ;-)

+0

Grazie per lo sforzo. Nel mio caso era un grafo di oggetti molto più complesso, centinaia di essi. Per ovvi motivi non ho potuto postare tutti qui, basta tagliare lo scenario minimo per illustrare il punto. – mindas

+0

@mindas Hai provato a riordinare la definizione dei bean nel file? Prova a mettere il Foo al secondo o all'ultimo posto. – partlov

1

Per qualche motivo stiamo ottenendo questo a caso anche nei test e nei test di integrazione (versione primavera 4.1.4, java 1.8).

Sembra che ci potrebbe essere più di un colpevole - Autowiring sembrava causare questo in un primo momento.

Tuttavia, abbiamo risolto i guasti più consistenti assicurandoci di assegnare a ciascun bean interessato un campo "id".