W zależności od konkretnych wymagań w niektórych przypadkach mechanizm modułu ładującego usługi Java może osiągnąć to, czego szukasz.
Krótko mówiąc, pozwala programistom jawnie zadeklarować, że klasa podklasuje jakąś inną klasę (lub implementuje jakiś interfejs), umieszczając ją w pliku w katalogu pliku JAR / WAR META-INF/services. Można go następnie odkryć za pomocą java.util.ServiceLoaderklasy, która po otrzymaniu Classobiektu wygeneruje instancje wszystkich zadeklarowanych podklas tej klasy (lub, jeśli Classreprezentuje interfejs, wszystkie klasy implementujące ten interfejs).
Główną zaletą tego podejścia jest to, że nie ma potrzeby ręcznego skanowania całej ścieżki klasy w poszukiwaniu podklas - cała logika wykrywania jest zawarta w ServiceLoaderklasie i ładuje tylko klasy jawnie zadeklarowane w META-INF/serviceskatalogu (nie każda klasa w ścieżce klasy) .
Istnieją jednak pewne wady:
- Nie znajdzie wszystkich podklas, tylko te, które są wyraźnie zadeklarowane. Jako takie, jeśli naprawdę potrzebujesz znaleźć wszystkie podklasy, takie podejście może być niewystarczające.
- Wymaga od programisty jawnego zadeklarowania klasy w
META-INF/serviceskatalogu. Jest to dodatkowe obciążenie dla programisty i może być podatne na błędy.
ServiceLoader.iterator()Generuje podklasy przykłady, a nie ich Classobiektami. Powoduje to dwa problemy:
- Nie masz nic do powiedzenia na temat budowy podklas - do tworzenia instancji służy konstruktor no-arg.
- Jako takie, podklasy muszą mieć domyślny konstruktor, lub jawność deklarować konstruktor bez arg.
Najwyraźniej Java 9 rozwiąże niektóre z tych niedociągnięć (w szczególności te dotyczące tworzenia podklas).
Przykład
Załóżmy, że chcesz znaleźć klasy, które implementują interfejs com.example.Example:
package com.example;
public interface Example {
public String getStr();
}
Klasa com.example.ExampleImplimplementuje ten interfejs:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Zadeklarowałbyś, że klasa ExampleImpljest implementacją Examplepoprzez utworzenie pliku META-INF/services/com.example.Examplezawierającego tekst com.example.ExampleImpl.
Następnie można uzyskać instancję każdej implementacji Example(w tym instancję ExampleImpl) w następujący sposób:
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.