web-dev-qa-db-de.com

Richtige Art und Weise, aus einem Singleton eine Spring-Bohne zu machen

Ich konvertiere ein Singleton in ein Spring-Bean, sodass der Spring-Kontext der gesamten Webanwendung nicht ordnungsgemäß geladen wird, wenn das Singleton nicht initialisiert wird. 

Wenn der Spring-Kontext nicht ordnungsgemäß geladen wird, hat dies den Vorteil, dass Benutzer die Konfiguration während der Bereitstellung selbst bemerken und korrigieren. Im Gegensatz zur Verwendung von "Nicht-Spring-Bean-Singleton": Wenn dies während der Initialisierung eine Ausnahme auslöst, merkt das niemand ... bis ein Benutzer sich über fehlende Funktionalität beschwert.

Meine Änderungen funktionieren wie erwartet .. aber ich bin nicht sicher, ob ich das Richtige tue.
Irgendwelche Gedanken?

Der Code sieht so aus:

public class MySingleton {

    private static MySingleton INSTANCE = null;
    private MySingleton(){}


public static MySingleton getInstance(){
    if(INSTANCE == null){
        synchronized(MySingleton.class){
            if(INSTANCE == null){
                try{
                    doWork()
                }catch(Exception e){
                    throw new IllegalStateException("xyz", e);
                }
                INSTANCE = new MySingleton();
            }
        }
    }

    return INSTANCE;
}

private static void doWork() {
    // do some work
    }

}

Und in der Frühlings-Config-XML wird die Bean definiert als:

<bean id="MySingletonBean"
    class="com.MySingleton"
    factory-method="getInstance" lazy-init="false" singleton="true">
</bean>

Anmerkung: Das meiste davon ähnelt der in diesem Artikel beschriebenen Strategie: http://springtips.blogspot.com/2007/06/configuration-hell-remedy-with.html


Edit 1:

Die Klassen, die dieses Singleton verwenden, sind keine Spring-Beans selbst. Sie sind nur Nicht-Spring-Pojos, die ich nicht in den Frühling umwandeln kann. Sie müssen sich auf die getInstance () - Methode verlassen, um den Singleton zu erreichen.


Edit 2: (Kopieren eines Kommentars, den ich unten in diesen Beschreibungsabschnitt aufgenommen habe) Ich versuche zwei Dinge anzuvisieren:

  1. Ich möchte, dass Spring den Singleton initialisiert. Wenn also die Initialisierung fehlschlägt, schlägt das Laden der Anwendung fehl. 
  2. Ich möchte, dass die anderen Klassen Klassen verwenden können, ohne auf contextAwareObj.getBean ("MySingleton") angewiesen zu sein.


EDIT 3 (FINAL): Ich habe mich entschieden, diese Klasse zu einem Einzelspieler zu machen ... und mache es nicht zu einer Spring Bean. Wenn die Initialisierung fehlschlägt, wird etwas in der Protokolldatei protokolliert. Hoffentlich wird der Bereitstellungsmitarbeiter darauf aufmerksam gemacht. Ich habe den oben genannten Ansatz aufgegeben, da ich der Meinung bin, dass dies in Zukunft einen Albtraum für die Wartung schaffen wird, also musste ich es tun Wählen Sie zwischen - Singleton - oder - Spring Bean. Ich habe mich für Singleton entschieden.

20
rk2010

Sie müssen das Feld INSTANCE als volatile deklarieren, damit das gesperrte Sperren ordnungsgemäß funktioniert.

Siehe Effektives Java , Punkt 71 .

18
Matt Ball

Warum verwenden Sie singleton an erster Stelle? Lassen Sie Spring einfach Bean für Sie erstellen (mit dem Standardbereich singleton) und ... verwenden Sie es. Natürlich kann immer jemand die Bohne von Hand kreieren, aber das war in meinem Fall nie ein Problem.

Abhängigkeitseinspritzung und Spring-Managed Bean Lifecycle erleichtern das Leben erheblich (sehen Sie nur, wie viele Fallstricke Sie vermeiden können). Beachten Sie auch, dass Ausnahmen, die von der c-tor- oder @PostContruct-Methode ausgelöst werden, sich ausbreiten und der Start des Anwendungskontexts ebenfalls fehlschlägt.

UPDATE: Ich verstehe deinen Punkt. Dies ist, was mir in den Sinn kam:

@Service
public class Singleton {

    private static AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

    public Singleton() {
        final Singleton previous = INSTANCE.getAndSet(this);
        if(previous != null)
            throw new IllegalStateException("Second singleton " + this + " created after " + previous);
    }

    public static Singleton getInstance() {
        return INSTANCE.get();
    }

}

Und lassen Sie Spring seine Arbeit machen. Sie können DI wenn möglich verwenden und Singleton.getInstance() dort, wo Sie müssen.

Es gibt auch mehr Hardcore-Lösungen, wie das AspectJ-Weben zum Kompilieren und das Einspritzen von Spring-Beans.

12

Ich bin nicht sicher, warum Sie das tun wollen. Wenn Sie Spring sagen, dass eine Bean ein Singleton sein sollte, muss die entsprechende Klasse kein Singleton sein und keine Factory. Der Frühling erzeugt einfach immer nur eine Instanz.

Der verlinkte Artikel macht für mich keinen Sinn, da KEINE Injektion stattfindet, die ich sehen kann: "AnyService" ruft die Singleton Factory-Methode auf; Dass das Singleton im App-Kontext referenziert wird, ist irrelevant, bis es referenziert wird, und es scheint, dass keine andere Bean darauf verweist.

6
Rodney Gitzel

Echte Singleton sind schwer zu arbeiten.

Das flüchtige, doppelt geprüfte Sperren funktioniert auch nicht. Lesen Sie darüber im Wiki http://en.wikipedia.org/wiki/Double-checked_locking

Am besten machen Sie das einfach

public class MySingleton {

    private static MySingleton INSTANCE = new MySingleton();

Dies ist der Fall, wenn Sie in Ihrem echten Code keine Konstruktorparameter haben.

5
Denis

Meiner Meinung nach handelt es sich hierbei um eine Gürtel- und Hosenträgerlösung.

Wenn Sie eine Bean erstellen und in der Konfiguration als Singleton deklarieren, muss die Bean nicht vor einer mehrfachen Erstellung geschützt werden.

Sie schützen sich jetzt grundsätzlich vor jemandem, der die Bean falsch konfiguriert hat.

Ich persönlich würde das durch Dokumentation in der Federkonfiguration und Javadoc "lösen".

3
Peter Tillemans

Um Code beim Start auszuführen (und bei einem Fehler fehlzuschlagen), verwenden Sie eine der vielen Möglichkeiten, um Startereignisse zu registrieren, z. siehe http://www.baeldung.com/running-setup-logic-on-startup-in-spring

Beispiel:

@Component
public class InitializingBeanExampleBean implements InitializingBean {

    private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class);

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}
1
tkruse