Wie erstelle ich einen JVM-Global Singleton?

8

Ich wurde von dieser Stack-Overflow-Frage inspiriert >

Wie kann man eine Java-Klasseninstanz erstellen, die garantiert nur einmal für den gesamten JVM-Prozess verfügbar ist? Jede Anwendung, die auf dieser JVM ausgeführt wird, sollte dann diese Singleton-Instanz verwenden können.

    
Njax3SmmM2x2a0Zf7Hpd 03.05.2014, 14:02
quelle

1 Antwort

11

Sie können tatsächlich ein solches Singleton implementieren. Das Problem, das Ihnen in den Kommentaren beschrieben wurde, ist die Möglichkeit, dass eine Klasse von mehreren ClassLoader s geladen wird. Jedes dieser ClassLoader s kann dann eine Klasse mit identischem Namen definieren, die fälschlicherweise als eindeutig angenommen wird.

Sie können dies jedoch vermeiden, indem Sie einen Accessor für Ihr Singleton implementieren, der explizit darauf angewiesen ist, eine bestimmte ClassLoader für eine Klasse eines bestimmten Namens zu überprüfen, die wiederum Ihr Singleton enthält. Auf diese Weise können Sie vermeiden, dass eine Singleton-Instanz von zwei verschiedenen ClassLoader s bereitgestellt wird und die Instanz dupliziert wird, die in der gesamten JVM eindeutig sein muss.

Aus Gründen, die später erklärt werden, teilen wir die Singleton und die SingletonAccessor in zwei verschiedene Klassen auf. Für die folgende Klasse müssen wir später sicherstellen, dass wir immer mit einem bestimmten ClassLoader darauf zugreifen:

%Vor%

Ein praktischer Wert für ClassLoader ist der Systemklassenlader. Der Systemklassenlader kennt alle Klassen im JVM-Klassenpfad und hat per Definition die Erweiterung und die Bootstrap-Klassenlader als Eltern. Diese beiden Klassenladeprogramme kennen normalerweise keine domänenspezifischen Klassen wie unsere Singleton -Klasse. Dies schützt uns vor unerwünschten Überraschungen. Außerdem wissen wir, dass es in einer laufenden Instanz der JVM global zugänglich und bekannt ist.

Nehmen wir an, dass die Klasse Singleton im Klassenpfad ist. Auf diese Weise können wir die Instanz von diesem Accessor mit Reflektion empfangen:

%Vor%

Indem wir angeben, dass wir pkg.Singleton explizit vom Systemklassenlader laden wollen, stellen wir sicher, dass wir immer dieselbe Instanz erhalten, obwohl der Klassenlader unser SingletonAccessor geladen hat. Im obigen Beispiel stellen wir zusätzlich sicher, dass Singleton nur einmal instanziiert wird. Alternativ können Sie die Instanziierungslogik in die Singleton -Klasse selbst einfügen und die unbenutzten Instanzen verrotten lassen, falls andere Singleton -Klassen jemals geladen werden.

Es gibt jedoch einen großen Nachteil. Sie vermissen alle Möglichkeiten der Typsicherheit, da Sie nicht davon ausgehen können, dass Ihr Code immer von ClassLoader ausgeführt wird, was das Laden von Klassen von Singleton auf den Systemklassenlader delegiert. Dies trifft insbesondere auf eine Anwendung zu, die auf einem Anwendungsserver ausgeführt wird, der häufig Child-First-Semantiken für seine Klassenladeprogramme implementiert und nicht den Systemklassenlader nach bekannten Typen fragt, aber zuerst versucht, seine eigenen Typen zu laden . Beachten Sie, dass ein Laufzeittyp durch zwei Merkmale gekennzeichnet ist:

  1. Sein vollständig qualifizierter Name
  2. Sein ClassLoader

Aus diesem Grund muss die Methode SingletonAccessor::get Object anstelle von Singleton zurückgeben.

Ein weiterer Nachteil ist die Tatsache, dass der Singleton -Typ im Klassenpfad gefunden werden muss, damit dies funktioniert. Andernfalls kennt der Systemklassenlader diesen Typ nicht. Wenn Sie den Singleton -Typ auf den Klassenpfad setzen können, sind Sie hier fertig. Keine Probleme.

Wenn Sie das nicht machen können, gibt es einen anderen Weg, indem Sie zum Beispiel meine Codegenerierungsbibliothek Byte Buddy verwenden. Mit dieser Bibliothek können wir einfach einen solchen Typ zur Laufzeit definieren und in den Systemklassenlader einspeisen:

%Vor%

Sie haben gerade eine Klasse pkg.Singleton für den Systemklassenlader definiert und die obige Strategie ist wieder anwendbar.

Außerdem können Sie die Typsicherheitsprobleme vermeiden, indem Sie einen Wrappertyp implementieren. Sie können dies auch mit Hilfe von Byte Buddy automatisieren:

%Vor%

Sie haben gerade einen Delegator erstellt, der alle Methoden der Klasse Singleton überschreibt und ihren Aufruf an Aufrufe der JVM-globalen Singleton-Instanz delegiert. Beachten Sie, dass wir die reflektiven Methoden erneut laden müssen, obwohl sie signaturidentisch sind, da wir uns nicht darauf verlassen können, dass ClassLoader s des Delegaten und die JVM-globalen Klassen identisch sind.

In der Praxis möchten Sie vielleicht die Aufrufe von SingletonAccessor.get() zwischenspeichern und vielleicht sogar die reflektiven Methoden-Lookups (die im Vergleich zu den reflektiven Methodenaufrufen ziemlich teuer sind). Dieser Bedarf hängt jedoch stark von Ihrer Anwendungsdomäne ab. Wenn Sie Probleme mit Ihrer Konstruktorhierarchie haben, können Sie die Methodensignaturen auch in eine Schnittstelle ausklammern und diese Schnittstelle sowohl für den obigen Accessor als auch für Ihre Singleton -Klasse implementieren.

    
Rafael Winterhalter 12.08.2014, 20:22
quelle

Tags und Links