it-swarm.com.de

Spring spritzt einen statischen (globalen) Singleton ein

Ich habe eine Klasse, die so aussieht:

public class Configurator {
    private static Configurator INSTANCE = null;

    private int maxRange = 1;

    // many other properties; each property has a default value

    private static synchronized Configurator getInstance() {
        if(INSTANCE == null)
            return new Configurator();

        return INSTANCE;
    }

    public static int getMaxRange() {
        getInstance().maxRange;
    }

    public static void setMaxRange(int range) {
        getInstance().maxRange = range;
    }

    // Getters and setters for all properties follow this pattern
}

Es dient als globales Konfigurationsobjekt, das beim Start der App festgelegt werden kann, und wird dann von Dutzenden von Klassen im gesamten Projekt verwendet:

// Called at app startup to configure everything
public class AppRunner {
    Configurator.setMaxRange(30);
}

// Example of Configurator being used by another class
public class WidgetFactory {
    public void doSomething() {
        if(Configurator.getMaxRange() < 50)
            // do A
        else
            // do B
    }
}

Ich importiere diesen Code jetzt in ein Spring-Projekt und versuche, meine Sprinig-XML (Beans) zu konfigurieren. Meine Vermutung ist, dass ich eine einsame Configurator-Bean wie (oder ähnliches) definieren könnte:

<bean id="configurator" class="com.me.myapp.Configurator" scope="singleton">
    <property name="maxRange" value="30"/>
    <!-- etc., for all properties -->
</bean>

Auf diese Weise hat Spring, wenn WidgetFactory#doSomething ausgeführt wird, die Configurator-Klasse bereits geladen und zuvor konfiguriert.

Ist es richtig für mich, den scope="singleton" einzustellen, oder spielt dies keine Rolle? Stelle ich die statischen Eigenschaften richtig ein? Gibt es noch etwas, was ich hier tun oder in Betracht ziehen muss? Danke im Voraus.

11
user1768830

Es gibt einige Unterschiede zwischen Singleton als Designmuster und der Singleton-Einrichtung von Spring. Singleton als Entwurfsmuster stellt sicher, dass Sie pro Klassenlader ein Klassenobjekt definieren. Im Gegensatz dazu definiert Spring's Singleton-Funktion (und -Ansatz) eine Instanz pro Spring Context. 

In diesem Fall können Sie Ihre getInstance()-Methode verwenden, um von Spring zum Abrufen Ihrer Objektinstanz verwendet zu werden:

<bean id="configurator" class="com.me.myapp.Configurator" factory-method="getInstance">
</bean>

Bei Spring ist der Bereich singleton bean die Standardeinstellung, daher müssen Sie ihn nicht definieren.

Wenn Sie configurator als Spring-Bean verwenden möchten, müssen Sie sie in andere Objekte einfügen. Verwenden Sie nicht getInstance(), um sie zu packen. Verwenden Sie also in anderen Spring-Beans @Autowired oder definieren Sie den Verweis auf die Bean durch die XML-Datei. Wenn Sie die Verwendung von configurator in anderen Klassen nicht neu organisieren, wird es keinen Unterschied geben. Spring wird Ihre Klasse instanziieren, aber Sie werden sie wie zuvor verwenden.

Ich habe auch gesehen, dass Sie beim Design Ihres Singleton einen Fehler haben. Ihre getInstance()-Methode sollte öffentlich sein und andere Methoden sollten nicht statisch sein. In dem von Ihnen verwendeten Beispiel sollten Sie Singleton folgendermaßen verwenden:

Configurator.getInstance().someMethod()

In diesem Fall verwenden Sie einfach die Singleton-Klasse, ohne Objekte zu instantiieren! Weitere Informationen zum Singleton-Entwurfsmuster und seiner Verwendung finden Sie unter wikipedia zu Singleton (mit Java-Beispiel) .


HINWEIS: Es ist wert zu wissen und zu versuchen, Configurator als Singleton zu verwenden und Spring's Singleton-Funktion zu nutzen. Wenn Sie dies tun, werden die Vorteile sein, die Sie können

  • Entfernen Sie die getInstance()-Methode 
  • Machen Sie Ihren Konstruktor öffentlich 
  • Lassen Sie den Frühling dieses einzelne Objekt instanziieren.
10
partlov

Beans sind standardmäßig Singleton. Diese/weitere Informationen finden Sie auf der Spring-Website.

Sie sollten einen neuen Configurator in getInstance nicht instanziieren, da er sich nicht auf die federbelastete Bean bezieht und dies zu ernsthaften Problemen führen kann. Sie können diese Bean anschliessen und sie dann in Ruhe lassen. Sie wird nicht null sein, da Sie sie verdrahtet haben (und wenn dies der Fall ist, ist Ihr Programm nicht initialisiert worden).

1
Bob Flannigon

Ja, wenn Sie etwas globales wollen, ist der Singleton-Bereich die richtige Option. Ein paar erwähnenswerte Dinge sind hier: 

  1. Der Standardbereich im Frühjahr ist Singleton, daher müssen Sie Ihre Bean nicht explizit als Singleton-Bereich festlegen.
  2. Bei der Verwendung von Spring müssen Sie keinen Singleton-MusterStil-Code schreiben, z. B. private Instanzen und Factory-Methoden. Das liegt daran, dass Spring Garantiert, dass es nur eine Instanz pro Spring Container gibt. Nicht einmal zu sagen, Ihre Fabrikmethode ist privat.
0
spiritwalker

Übrigens: das ist nicht Thread-sicher:

if(INSTANCE == null)
        return new Configurator();

    return INSTANCE;
}

Während dies wäre:

private static Configurator INSTANCE = new Configurator();

(Eifrige Initialisierung)

private static volatile Singleton _instance = null;

(Faule Initialisierung mit volatilem Schlüsselwort)

Es hat damit zu tun, wie Java Speicher zuordnet und Instanzen erstellt. Dies ist nicht atomar, sondern erfolgt in zwei Schritten und kann vom Thread-Scheduler gestört werden.

Siehe auch http://regrecall.blogspot.de/2012/05/Java-singleton-pattern-thread-safe.html .

0
Dennis Löhmann