Schema di progettazione Singleton vs fagioli Singleton nel contenitore Spring


90

Come tutti sappiamo, abbiamo i bean come singleton per impostazione predefinita nel contenitore Spring e se abbiamo un'applicazione web basata sul framework Spring, allora in quel caso dobbiamo davvero implementare il modello di progettazione Singleton per contenere dati globali piuttosto che creare semplicemente un bean attraverso la primavera .

Per favore, abbi pazienza se non sono in grado di spiegare cosa intendevo davvero chiedere.

Risposte:


60

Un fagiolo singleton in primavera e il pattern singleton sono abbastanza diversi. Il pattern Singleton dice che una e una sola istanza di una particolare classe verrà mai creata per classloader.

Lo scopo di un singleton Spring è descritto come "per container per bean". È l'ambito della definizione del bean per una singola istanza di oggetto per contenitore Spring IoC. L'ambito predefinito in Spring è Singleton.

Anche se l'ambito predefinito è singleton, è possibile modificare l'ambito del bean specificando l'attributo scope <bean ../>dell'elemento.

<bean id=".." class=".." scope="prototype" />

12
@ user184794: per container per bean, il che significa che c'è un solo classloader nel container spring. se ci sono due o più classloader nel container spring, ogni classloader avrà la propria istanza. Significa "per container per classloader per bean". gentilmente chiarire !!
Programmatore morto

4
Penso che significhi che un contenitore Spring utilizzerà un singolo classloader di cui è proprietario. ciò che fai al di fuori del meccanismo di Spring non è rilevante, cioè puoi creare i tuoi propri classloader e creare tutte le istanze di una classe che desideri, ma se passi attraverso il contenitore Spring, non creerà più di un'istanza
ino

1
Quindi non sono "abbastanza diversi" come dici tu. L'unica differenza è l'ambito - Spring container verses classloader
Zack Macomber

30

L'ambito singleton in spring significa singola istanza in un contesto Spring.
Il contenitore Spring restituisce semplicemente la stessa istanza più e più volte per le chiamate successive per ottenere il bean.


E la primavera non si preoccupa se la classe del bean è codificata come singleton o no, infatti se la classe è codificata come singleton il cui costruttore come privato, Spring usa BeanUtils.instantiateClass (javadoc qui ) per impostare il costruttore su accessibile e invocare esso.

In alternativa, possiamo usare un attributo metodo factory nella definizione di bean come questa

    <bean id="exampleBean" class="example.Singleton"  factory-method="getInstance"/>

1
sei sicuro di aver bisogno dell'attributo factory-method? sono abbastanza sicuro che Spring sappia come ottenere un'istanza anche se il costruttore è privato (probabilmente prova a chiamare getInstance)
inor

Una discussione correlata su come Spring invoca qui il
Xiawei Zhang

21

Facciamo l'esempio più semplice: hai un'applicazione e usi solo il classloader predefinito. Hai una classe che, per qualsiasi motivo, decidi che non dovrebbe avere più di un'istanza nell'applicazione. (Pensa a uno scenario in cui più persone lavorano su parti dell'applicazione).

Se non stai usando il framework Spring, il pattern Singleton garantisce che non ci sarà più di un'istanza di una classe nella tua applicazione. Questo perché non è possibile creare istanze della classe eseguendo "new" perché il costruttore è privato. L'unico modo per ottenere un'istanza della classe è chiamare un metodo statico della classe (solitamente chiamato "getInstance") che restituisce sempre la stessa istanza.

Dire che stai usando il framework Spring nella tua applicazione, significa semplicemente che oltre ai modi regolari per ottenere un'istanza della classe (metodi nuovi o statici che restituiscono un'istanza della classe), puoi anche chiedere a Spring di farti un'istanza di quella classe e Spring farà in modo che ogni volta che chiedi un'istanza di quella classe restituirà sempre la stessa istanza, anche se non hai scritto la classe utilizzando il pattern Singleton. In altre parole, anche se la classe ha un costruttore pubblico, se chiedi sempre a Spring un'istanza di quella classe, Spring chiamerà quel costruttore solo una volta durante la vita dell'applicazione.

Normalmente, se stai usando Spring, dovresti usare Spring solo per creare istanze e puoi avere un costruttore pubblico per la classe. Ma se il tuo costruttore non è privato, non stai realmente impedendo a nessuno di creare direttamente nuove istanze della classe, bypassando Spring.

Se vuoi veramente una singola istanza della classe, anche se usi Spring nella tua applicazione e definisci la classe in Spring come singleton, l'unico modo per assicurarti che sia implementare anche la classe usando il pattern Singleton. Ciò garantisce che ci sarà una singola istanza, indipendentemente dal fatto che le persone utilizzino Spring per ottenere un'istanza o ignorino Spring.


13

Trovo " per contenitore per fagiolo" difficile da apprendere . Direi " un bean per bean id in un contenitore ". Facciamo un esempio per capirlo. Abbiamo un esempio di classe di fagioli. Ho definito due bean di questa classe nella definizione di bean, come:

<bean id="id1" class="com.example.Sample" scope="singleton">
        <property name="name" value="James Bond 001"/>    
</bean>    
<bean id="id7" class="com.example.Sample" scope="singleton">
        <property name="name" value="James Bond 007"/>    
</bean>

Quindi, ogni volta che cerco di ottenere il bean con id "id1", il contenitore spring creerà un bean, lo memorizzerà nella cache e restituirà lo stesso bean a cui si è mai fatto riferimento con id1. Se provo a ottenerlo con id7, verrà creato un altro bean dalla classe Sample, lo stesso verrà memorizzato nella cache e restituito ogni volta che lo fai riferimento con id7.

Questo è improbabile con il pattern Singleton. Nel pattern Singlton viene sempre creato un oggetto per class loader. Tuttavia, in primavera, impostare l'ambito come Singleton non impedisce al contenitore di creare molte istanze da quella classe. Limita solo la creazione di nuovi oggetti per lo stesso ID, restituendo oggetti creati in precedenza quando viene richiesto un oggetto per lo stesso ID . Riferimento


Ben spiegato. Grazie!
Swapnil

12

L'ambito Singleton in Spring significa che questo bean verrà istanziato solo una volta entro Spring. In contrasto con l'ambito del prototipo (nuova istanza ogni volta), l'ambito della richiesta (una volta per richiesta), l'ambito della sessione (una volta per sessione HTTP).

L'ambito singleton non ha tecnicamente nulla a che fare con il modello di progettazione singleton. Non è necessario implementare i bean come singleton per inserirli nell'ambito singleton.


1
Correggimi se sbaglio quindi secondo te se ho bisogno di implementare qualsiasi oggetto come singleton quindi non è necessario implementare il pattern singleton. La creazione di quel fagiolo usando Spring funzionerà. Sono un po 'confuso ora con la mia comprensione relativa al modello di progettazione Singleton e all'ambito Singleton nel framework Spring.
Peeyush

1
La primavera non ti obbliga a usare il pattern Singleton.
lexicore

2

I fagioli Singleton in primavera e le classi basate sul modello di progettazione Singleton sono piuttosto diverse.

Il pattern singleton garantisce che una e una sola istanza di una particolare classe verrà mai creata per classloader dove l'ambito di un bean singleton di Spring è descritto come "per container per bean". L'ambito Singleton in Spring significa che questo bean verrà istanziato solo una volta entro Spring. Il contenitore Spring restituisce semplicemente la stessa istanza più e più volte per le chiamate successive per ottenere il bean.


13
Sei il "java maverick", giusto? Ciò renderebbe la tua affermazione "Ho trovato una buona spiegazione ed esempio in ..." un tentativo disonesto di nascondere che ti stai collegando al tuo sito web. Il tuo link non sembra essere importante per la risposta, comunque. Lo sto rimuovendo, per evitare che la risposta venga cancellata come spam. Si prega di leggere le FAQ sull'autopromozione prima di pubblicare altri link al proprio sito web. tieni anche presente che va bene per te inserire il link del tuo sito web nel tuo profilo.
Andrew Barber,

2

C'è una differenza fondamentale tra i due. Nel caso del modello di progettazione Singleton, verrà creata solo un'istanza di una classe per classLoader mentre ciò non è il caso con Spring singleton poiché viene creata l'ultima istanza di bean condivisa per l'id dato per contenitore IoC.

Ad esempio, se ho una classe con il nome "SpringTest" e il mio file XML ha un aspetto simile a questo: -

<bean id="test1" class="com.SpringTest" scope="singleton">
        --some properties here
</bean>    
<bean id="test2" class="com.SpringTest" scope="singleton">
        --some properties here   
</bean>

Quindi ora nella classe principale se controlli il riferimento dei due precedenti restituirà falso come secondo la documentazione di Spring: -

Quando un bean è un singleton, verrà gestita solo un'istanza condivisa del bean e tutte le richieste di bean con uno o più ID corrispondenti a quella definizione di bean risulteranno in quella specifica istanza di bean restituita dal contenitore Spring

Quindi, come nel nostro caso, le classi sono le stesse ma gli ID che abbiamo fornito sono diversi, quindi vengono create due istanze diverse.


1

"singleton" in primavera utilizza l'istanza get di bean factory, quindi la memorizza nella cache; quale modello di progettazione singleton è strettamente, l'istanza può essere recuperata solo dal metodo get statico e l'oggetto non può mai essere istanziato pubblicamente.


1

EX: "per contenitore per fagiolo".

        <bean id="myBean" class="com.spring4hibernate4.TestBean">
            <constructor-arg name="i" value="1"></constructor-arg>
            <property name="name" value="1-name"></property>
        </bean>

        <bean id="testBean" class="com.spring4hibernate4.TestBean">
            <constructor-arg name="i" value="10"></constructor-arg>
            <property name="name" value="10-name"></property>
        </bean>
    </beans>



    public class Test {

        @SuppressWarnings("resource")
        public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("ws.xml");
            TestBean teatBean = (TestBean) ac.getBean("testBean");
            TestBean myBean1 = (TestBean) ac.getBean("myBean");
            System.out.println("a : " + teatBean.test + " : "   + teatBean.getName());
            teatBean.setName("a TEST BEAN 1");
            System.out.println("uPdate : " + teatBean.test + " : "  + teatBean.getName());
            System.out.println("a1 : " + myBean1.test + " : " + myBean1.getName());
            myBean1.setName(" a1 TEST BEAN 10");
            System.out.println("a1 update : " + teatBean.test + " : " + myBean1.getName());
        }
    }

public class TestBean {
    public int test = 0;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name = "default";

    public TestBean(int i) {
        test += i;
    }
}

JAVA SINGLETON:

public class Singleton {
    private static Singleton singleton = new Singleton();
    private int i = 0;

    private Singleton() {
    }

    public static Singleton returnSingleton() {

        return singleton;
    }

    public void increment() {
        i++;
    }

    public int getInt() {
        return i;
    }
}

public static void main(String[] args) {
        System.out.println("Test");

        Singleton sin1 = Singleton.returnSingleton();
        sin1.increment();
        System.out.println(sin1.getInt());
        Singleton sin2 = Singleton.returnSingleton();
        System.out.println("Test");
        sin1.increment();
        System.out.println(sin1.getInt());
    }

<bean class = "com.spring4hibernate4.TestBean"> <constructor-arg name = "i" value = "1"> </constructor-arg> <property name = "name" value = "1-name"> </ proprietà> </bean> <bean class = "com.spring4hibernate4.TestBean"> <constructor-arg name = "i" value = "10"> </constructor-arg> <property name = "name" value = "10 -name "> </property> </bean> </beans>
Hariprasad

1

Il fagiolo singleton di primavera è descritto come "per contenitore per fagiolo". L'ambito Singleton in Spring significa che lo stesso oggetto nella stessa posizione di memoria verrà restituito allo stesso ID bean. Se si creano più bean di ID diversi della stessa classe, il contenitore restituirà oggetti diversi a ID diversi. È come una mappatura del valore della chiave in cui la chiave è l'ID del bean e il valore è l'oggetto del bean in un contenitore a molla. Dove il pattern Singleton garantisce che una e una sola istanza di una particolare classe verrà mai creata per classloader.


1

Tutte le risposte, almeno fino ad ora, si concentrano sulla spiegazione della differenza tra il modello di progettazione e il singleton Spring e non rispondono alla tua vera domanda: dovrebbe essere usato un modello di progettazione Singleton o un fagiolo singleton Spring? cos'è meglio?

Prima di rispondere, lasciatemi dire che potete fare entrambe le cose. È possibile implementare il bean come modello di progettazione Singleton e utilizzare Spring per iniettarlo nelle classi client come bean singleton Spring.

Ora, la risposta alla domanda è semplice: non utilizzare il design pattern Singleton!
Usa il singleton bean di Spring implementato come classe con il costruttore pubblico.
Perché? Perché il design pattern Singleton è considerato un anti-pattern. Principalmente perché complica i test. (E se non usi Spring per iniettarlo, tutte le classi che usano il singleton sono ora strettamente legate ad esso) e non puoi sostituirlo o estenderlo. Si può cercare su Google "Singleton anti-pattern" per avere maggiori informazioni su questo, ad esempio Singleton anti-pattern

Usare Spring singleton è la strada da percorrere (con un bean singleton implementato NON come un design pattern Singleton, ma piuttosto con un costruttore pubblico) in modo che il bean singleton Spring possa essere facilmente testato e le classi che lo utilizzano non siano strettamente accoppiate ad esso , ma piuttosto, Spring inietta il singleton (come interfaccia) in tutti i bean che ne hanno bisogno e il singleton può essere sostituito in qualsiasi momento con un'altra implementazione senza influire sulle classi client che lo utilizzano.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.