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/servicesVerzeichnis der JAR / WAR-Datei auflistet . Es kann dann mithilfe der java.util.ServiceLoaderKlasse ermittelt werden, die bei Angabe eines ClassObjekts Instanzen aller deklarierten Unterklassen dieser Klasse generiert (oder, wenn dies Classeine 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 ServiceLoaderKlasse enthalten und es werden nur die Klassen geladen, die explizit im META-INF/servicesVerzeichnis 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/servicesVerzeichnis explizit deklarieren . Dies ist eine zusätzliche Belastung für den Entwickler und kann fehleranfällig sein.
- Das
ServiceLoader.iterator()generiert Unterklasseninstanzen, nicht deren ClassObjekte. 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.ExampleImplimplementiert diese Schnittstelle:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Sie würden deklarieren, dass die Klasse ExampleImpleine Implementierung ist, Exampleindem Sie eine Datei erstellen, META-INF/services/com.example.Exampledie 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.