Abhängig von Ihren speziellen Anforderungen kann der Service Loader- Mechanismus von Java in einigen Fällen das erreichen, wonach Sie suchen.
Kurz gesagt, Entwickler können explizit deklarieren, dass eine Klasse eine andere Klasse unterklassifiziert (oder eine Schnittstelle implementiert), indem sie diese in einer Datei im META-INF/services
Verzeichnis der JAR / WAR-Datei auflistet . Es kann dann mithilfe der java.util.ServiceLoader
Klasse ermittelt werden, die bei Angabe eines Class
Objekts Instanzen aller deklarierten Unterklassen dieser Klasse generiert (oder, wenn dies Class
eine Schnittstelle darstellt, aller Klassen, die diese Schnittstelle implementieren).
Der Hauptvorteil dieses Ansatzes besteht darin, dass nicht der gesamte Klassenpfad manuell nach Unterklassen durchsucht werden muss. Die gesamte Erkennungslogik ist in der ServiceLoader
Klasse enthalten und es werden nur die Klassen geladen, die explizit im META-INF/services
Verzeichnis deklariert sind (nicht jede Klasse im Klassenpfad). .
Es gibt jedoch einige Nachteile:
- Es werden nicht alle Unterklassen gefunden, sondern nur diejenigen, die explizit deklariert sind. Wenn Sie also wirklich alle Unterklassen finden müssen, ist dieser Ansatz möglicherweise unzureichend.
- Der Entwickler muss die Klasse unter dem
META-INF/services
Verzeichnis explizit deklarieren . Dies ist eine zusätzliche Belastung für den Entwickler und kann fehleranfällig sein.
- Das
ServiceLoader.iterator()
generiert Unterklasseninstanzen, nicht deren Class
Objekte. Dies verursacht zwei Probleme:
- Sie können nicht sagen, wie die Unterklassen aufgebaut sind - der Konstruktor no-arg wird zum Erstellen der Instanzen verwendet.
- Daher müssen die Unterklassen einen Standardkonstruktor haben oder explizit einen Konstruktor ohne Argumente deklarieren.
Anscheinend wird Java 9 einige dieser Mängel beheben (insbesondere diejenigen, die die Instanziierung von Unterklassen betreffen).
Ein Beispiel
Angenommen, Sie möchten Klassen finden, die eine Schnittstelle implementieren com.example.Example
:
package com.example;
public interface Example {
public String getStr();
}
Die Klasse com.example.ExampleImpl
implementiert diese Schnittstelle:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Sie würden deklarieren, dass die Klasse ExampleImpl
eine Implementierung ist, Example
indem Sie eine Datei erstellen, META-INF/services/com.example.Example
die den Text enthält com.example.ExampleImpl
.
Dann könnten Sie eine Instanz jeder Implementierung von Example
(einschließlich einer Instanz von ExampleImpl
) wie folgt erhalten:
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
System.out.println(example.getStr());
}
// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.