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.ServiceLoader
klasy, która po otrzymaniu Class
obiektu wygeneruje instancje wszystkich zadeklarowanych podklas tej klasy (lub, jeśli Class
reprezentuje 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 ServiceLoader
klasie i ładuje tylko klasy jawnie zadeklarowane w META-INF/services
katalogu (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/services
katalogu. Jest to dodatkowe obciążenie dla programisty i może być podatne na błędy.
ServiceLoader.iterator()
Generuje podklasy przykłady, a nie ich Class
obiektami. 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.ExampleImpl
implementuje ten interfejs:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Zadeklarowałbyś, że klasa ExampleImpl
jest implementacją Example
poprzez utworzenie pliku META-INF/services/com.example.Example
zawierają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.