Singleton-Entwurfsmuster gegen Singleton-Bohnen im Frühlingsbehälter


90

Wie wir alle wissen, haben wir Beans standardmäßig als Singleton im Spring-Container. Wenn wir eine Webanwendung haben, die auf dem Spring-Framework basiert, müssen wir in diesem Fall wirklich das Singleton-Entwurfsmuster implementieren, um globale Daten zu speichern, anstatt nur eine Bean durch Spring zu erstellen .

Bitte nehmen Sie Kontakt mit mir auf, wenn ich nicht erklären kann, was ich eigentlich fragen wollte.

Antworten:


59

Eine Singleton-Bohne im Frühling und das Singleton-Muster sind sehr unterschiedlich. Das Singleton-Muster besagt, dass pro Klassenlader immer nur eine Instanz einer bestimmten Klasse erstellt wird.

Der Umfang eines Spring-Singletons wird als "pro Container pro Bean" beschrieben. Dies ist der Umfang der Bean-Definition für eine einzelne Objektinstanz pro Spring IoC-Container. Der Standardbereich in Spring ist Singleton.

Obwohl der Standardbereich Singleton ist, können Sie den Bereich der Bean ändern, indem Sie das Bereichsattribut des <bean ../>Elements angeben.

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

11
@ user184794: pro Container pro Bean, dh es gibt nur einen Klassenlader im Federcontainer. Wenn sich zwei oder mehr Klassenlader im Federcontainer befinden, hat jeder Klassenlader eine eigene Instanz. Bedeutet das "pro Container pro Klassenlader pro Bean"? bitte klären !!
Toter Programmierer

4
Ich denke, es bedeutet, dass ein Spring-Container einen einzelnen Klassenlader verwendet, den er besitzt. was Sie außerhalb des Mechanismus des Frühlings tun , ist nicht relevant, also können Sie Ihre eigenen Klassenladeprogramme erstellen und wie viele Instanzen einer Klasse erstellen , wie Sie wollen, aber wenn Sie durch den Frühling Container gehen, wird es nicht mehr als eine Instanz erstellen
inor

1
Dann sind sie nicht "ganz anders", wie Sie sagen. Der einzige Unterschied ist Umfang - Spring Container versus Classloader
Zack Macomber

30

Singleton-Bereich im Frühjahr bedeutet einzelne Instanz in einem Spring-Kontext. Der
Spring-Container gibt lediglich immer wieder dieselbe Instanz für nachfolgende Aufrufe zurück, um die Bean abzurufen.


Und Spring stört es nicht, ob die Klasse der Bean als Singleton codiert ist oder nicht. Wenn die Klasse als Singleton codiert ist, dessen Konstruktor als privat bezeichnet wird, verwendet Spring BeanUtils.instantiateClass ( hier javadoc ), um den Konstruktor auf access und invoke zu setzen es.

Alternativ können wir in dieser Bean-Definition ein Factory-Methodenattribut verwenden

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

1
Sind Sie sicher, dass Sie das Factory-Methodenattribut benötigen? Ich bin mir ziemlich sicher, dass Spring weiß, wie man eine Instanz erhält, selbst wenn der Konstruktor privat ist (versucht wahrscheinlich, getInstance aufzurufen)
oder

Eine verwandte Diskussion darüber, wie Spring hier einen
anruft

21

Nehmen wir das einfachste Beispiel: Sie haben eine Anwendung und verwenden nur den Standardklassenlader. Sie haben eine Klasse, die aus irgendeinem Grund entscheidet, dass sie nicht mehr als eine Instanz in der Anwendung enthalten soll. (Stellen Sie sich ein Szenario vor, in dem mehrere Personen an Teilen der Anwendung arbeiten.)

Wenn Sie das Spring-Framework nicht verwenden, stellt das Singleton-Muster sicher, dass Ihre Anwendung nicht mehr als eine Instanz einer Klasse enthält. Dies liegt daran, dass Sie Instanzen der Klasse nicht instanziieren können, indem Sie 'new' ausführen, da der Konstruktor privat ist. Die einzige Möglichkeit, eine Instanz der Klasse abzurufen, besteht darin, eine statische Methode der Klasse (normalerweise 'getInstance' genannt) aufzurufen, die immer dieselbe Instanz zurückgibt.

Wenn Sie sagen, dass Sie das Spring-Framework in Ihrer Anwendung verwenden, bedeutet dies nur, dass Sie Spring zusätzlich zu den regulären Methoden zum Abrufen einer Instanz der Klasse (neue oder statische Methoden, die eine Instanz der Klasse zurückgeben) auch bitten können, Sie zu erhalten Eine Instanz dieser Klasse und Spring stellen sicher, dass immer dann, wenn Sie nach einer Instanz dieser Klasse fragen, dieselbe Instanz zurückgegeben wird, auch wenn Sie die Klasse nicht mit dem Singleton-Muster geschrieben haben. Mit anderen Worten, selbst wenn die Klasse einen öffentlichen Konstruktor hat und Spring immer nach einer Instanz dieser Klasse fragt, ruft Spring diesen Konstruktor während der Lebensdauer Ihrer Anwendung nur einmal auf.

Wenn Sie Spring verwenden, sollten Sie Spring normalerweise nur zum Erstellen von Instanzen verwenden, und Sie können einen öffentlichen Konstruktor für die Klasse haben. Wenn Ihr Konstruktor jedoch nicht privat ist, verhindern Sie nicht wirklich, dass jemand direkt neue Instanzen der Klasse erstellt, indem Sie Spring umgehen.

Wenn Sie wirklich eine einzelne Instanz der Klasse möchten, auch wenn Sie Spring in Ihrer Anwendung verwenden und die Klasse in Spring als Singleton definieren, können Sie nur sicherstellen, dass die Klasse auch mithilfe des Singleton-Musters implementiert wird. Dies stellt sicher, dass es eine einzelne Instanz gibt, unabhängig davon, ob Benutzer Spring verwenden, um eine Instanz abzurufen, oder Spring umgehen.


12

Singleton-Bereich im Frühjahr bedeutet, dass diese Bean von Spring nur einmal instanziiert wird. Im Gegensatz zum Prototypbereich (jedes Mal neue Instanz), Anforderungsbereich (einmal pro Anforderung), Sitzungsbereich (einmal pro HTTP-Sitzung).

Der Singleton-Bereich hat technisch nichts mit dem Singleton-Entwurfsmuster zu tun. Sie müssen Ihre Beans nicht als Singletons implementieren, damit sie in den Singleton-Bereich eingefügt werden.


1
Korrigieren Sie mich, wenn ich falsch liege, wenn ich ein Objekt als Singleton implementieren muss, sodass kein Singleton-Muster implementiert werden muss. Das Erstellen dieser Bean mit Spring funktioniert. Ich bin jetzt ein bisschen verwirrt mit meinem Verständnis in Bezug auf das Singleton-Designmuster und den Singleton-Bereich im Spring-Framework.
Peeyush

1
Spring zwingt Sie nicht, das Singleton-Muster zu verwenden.
Lexicore

11

Ich finde " pro Behälter pro Bohne" schwer zu verstehen . Ich würde sagen " eine Bean pro Bean-ID in einem Container ". Lassen Sie uns ein Beispiel haben, um es zu verstehen. Wir haben eine Bohnenklassenprobe. Ich habe zwei Bohnen aus dieser Klasse in der Bohnendefinition definiert, wie:

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

Wenn ich also versuche, die Bean mit der ID "id1" abzurufen, erstellt der Spring-Container eine Bean, speichert sie im Cache und gibt dieselbe Bean zurück, auf die jemals mit id1 verwiesen wurde. Wenn ich versuche, es mit id7 abzurufen, wird eine weitere Bean aus der Sample-Klasse erstellt. Diese wird jedes Mal zwischengespeichert und zurückgegeben, wenn Sie dies mit id7 referenziert haben.

Dies ist bei Singleton-Mustern unwahrscheinlich. Im Singlton-Muster wird immer ein Objekt pro Klassenlader erstellt. Wenn Sie jedoch im Frühjahr den Bereich als Singleton festlegen, kann der Container nicht viele Instanzen aus dieser Klasse erstellen. Es schränkt lediglich die Erstellung neuer Objekte für dieselbe ID erneut ein und gibt das zuvor erstellte Objekt zurück, wenn ein Objekt für dieselbe ID angefordert wird . Referenz


Gut erklärt. Vielen Dank!
Swapnil

2

Singleton-Bohnen im Frühjahr und Klassen, die auf dem Singleton-Entwurfsmuster basieren, sind sehr unterschiedlich.

Das Singleton-Muster stellt sicher, dass pro Klassenlader immer nur eine Instanz einer bestimmten Klasse erstellt wird, wobei der Umfang einer Spring-Singleton-Bean als "pro Container pro Bean" beschrieben wird. Singleton-Bereich im Frühjahr bedeutet, dass diese Bean von Spring nur einmal instanziiert wird. Spring Container gibt lediglich immer wieder dieselbe Instanz für nachfolgende Aufrufe zurück, um die Bean abzurufen.


13
Du bist der "Java-Außenseiter", oder? Das würde Ihre Aussage "Eine gute Erklärung und ein gutes Beispiel gefunden bei ..." zu einem unehrlichen Versuch machen, zu verbergen, dass Sie auf Ihre eigene Website verlinken. Ihr Link scheint für die Antwort sowieso nicht wichtig zu sein. Ich entferne es, um zu vermeiden, dass die Antwort als Spam gelöscht wird. Bitte lesen Sie die FAQ zur Eigenwerbung, bevor Sie weitere Links zu Ihrer Website veröffentlichen. Beachten Sie auch, dass es für Sie in Ordnung ist, Ihren Website-Link in Ihr Profil aufzunehmen.
Andrew Barber

2

Es gibt einen sehr grundlegenden Unterschied zwischen den beiden. Im Fall eines Singleton-Entwurfsmusters wird nur eine Instanz einer Klasse pro classLoader erstellt, während dies bei Spring Singleton nicht der Fall ist, da in der späteren Version eine gemeinsam genutzte Bean-Instanz für die angegebene ID pro IoC-Container erstellt wird.

Wenn ich zum Beispiel eine Klasse mit dem Namen "SpringTest" habe und meine XML-Datei ungefähr so ​​aussieht: -

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

Wenn Sie also in der Hauptklasse die Referenz der beiden oben genannten überprüfen, wird gemäß der Spring-Dokumentation false zurückgegeben: -

Wenn eine Bean ein Singleton ist, wird nur eine gemeinsam genutzte Instanz der Bean verwaltet, und alle Anforderungen für Beans mit einer ID oder IDs, die dieser Bean-Definition entsprechen, führen dazu, dass diese eine bestimmte Bean-Instanz vom Spring-Container zurückgegeben wird

Wie in unserem Fall sind die Klassen gleich, aber die von uns angegebenen IDs sind unterschiedlich, sodass zwei verschiedene Instanzen erstellt werden.


1

"singleton" verwendet im Frühjahr die Bean Factory-Get-Instanz und speichert sie dann im Cache. Welches Singleton-Entwurfsmuster streng genommen ist, die Instanz kann nur von der statischen get-Methode abgerufen werden, und das Objekt kann niemals öffentlich instanziiert werden.


1

EX: "pro Behälter pro Bohne".

        <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"> </ Konstruktorarg> <property name = "name" value = "1-name"> </ property> </ bean> <bean class = "com.spring4hibernate4.TestBean"> <constructor-arg name = "i" value = "10"> </ Konstruktorarg> <property name = "name" value = "10 -name "> </ property> </ bean> </ bean>
Hariprasad

1

Spring Singleton Bean wird als "pro Container pro Bohne" beschrieben. Singleton-Bereich in Spring bedeutet, dass dasselbe Objekt am selben Speicherort an dieselbe Bean-ID zurückgegeben wird. Wenn mehrere Beans mit unterschiedlichen IDs derselben Klasse erstellt werden, gibt der Container unterschiedliche Objekte an unterschiedliche IDs zurück. Dies ist wie eine Schlüsselwertzuordnung, bei der der Schlüssel die Bean-ID und der Wert das Bean-Objekt in einem Federcontainer ist. Wobei als Singleton-Muster sichergestellt wird, dass pro Klassenlader immer nur eine Instanz einer bestimmten Klasse erstellt wird.


1

Zumindest bis jetzt konzentrieren sich alle Antworten darauf, den Unterschied zwischen dem Entwurfsmuster und dem Spring-Singleton zu erklären, und gehen nicht auf Ihre eigentliche Frage ein: Sollte ein Singleton-Entwurfsmuster oder eine Spring-Singleton-Bean verwendet werden? was ist besser?

Bevor ich antworte, möchte ich nur sagen, dass Sie beides können. Sie können die Bean als Singleton-Entwurfsmuster implementieren und Spring verwenden, um sie als Spring-Singleton-Bean in die Clientklassen einzufügen.

Die Antwort auf die Frage ist jetzt einfach: Verwenden Sie nicht das Singleton-Entwurfsmuster!
Verwenden Sie die Singleton-Bean von Spring, die als Klasse mit öffentlichem Konstruktor implementiert ist.
Warum? Weil das Singleton-Entwurfsmuster als Anti-Muster betrachtet wird. Meistens, weil es das Testen erschwert. (Und wenn Sie Spring nicht zum Injizieren verwenden, sind jetzt alle Klassen, die den Singleton verwenden, eng daran gebunden.) Sie können ihn nicht ersetzen oder erweitern. Man kann "Singleton Anti-Pattern" googeln, um weitere Informationen zu diesem Thema zu erhalten, z. B. Singleton Anti-Pattern

Die Verwendung von Spring-Singleton ist der richtige Weg (mit einer Singleton-Bean, die NICHT als Singleton-Entwurfsmuster implementiert ist, sondern mit einem öffentlichen Konstruktor), damit die Spring-Singleton-Bean leicht getestet werden kann und Klassen, die sie verwenden, nicht eng daran gekoppelt sind Stattdessen injiziert Spring den Singleton (als Schnittstelle) in alle Beans, die ihn benötigen, und der Singleton-Bean kann jederzeit durch eine andere Implementierung ersetzt werden, ohne dass dies Auswirkungen auf die Clientklassen hat, die ihn verwenden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.