Java Language
ServiceLoader
Szukaj…
Uwagi
ServiceLoader może służyć do uzyskiwania instancji klas rozszerzających dany typ (= usługa), które są określone w pliku spakowanym w pliku .jar . Usługa, która jest rozszerzana / wdrażana, jest często interfejsem, ale nie jest to wymagane.
Klasy rozszerzające / implementujące muszą zapewniać konstruktor zerowego argumentu, aby ServiceLoader mógł je utworzyć.
Aby zostać ServiceLoader przez ServiceLoader plik tekstowy z nazwą w pełni kwalifikowanej nazwy typu zaimplementowanej usługi musi być przechowywany w katalogu META-INF/services w pliku jar. Ten plik zawiera jedną w pełni kwalifikowaną nazwę klasy implementującej usługę w wierszu.
Usługa rejestratora
Poniższy przykład pokazuje, jak utworzyć instancję klasy do rejestrowania za pośrednictwem ServiceLoader .
Usługa
package servicetest;
import java.io.IOException;
public interface Logger extends AutoCloseable {
void log(String message) throws IOException;
}
Realizacje usługi
Następująca implementacja po prostu zapisuje komunikat do System.err
package servicetest.logger;
import servicetest.Logger;
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.err.println(message);
}
@Override
public void close() {
}
}
Następująca implementacja zapisuje komunikaty w pliku tekstowym:
package servicetest.logger;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import servicetest.Logger;
public class FileLogger implements Logger {
private final BufferedWriter writer;
public FileLogger() throws IOException {
writer = new BufferedWriter(new FileWriter("log.txt"));
}
@Override
public void log(String message) throws IOException {
writer.append(message);
writer.newLine();
}
@Override
public void close() throws IOException {
writer.close();
}
}
META-INF / services / servicetest.Logger
Plik META-INF/services/servicetest.Logger zawiera nazwy implementacji Logger .
servicetest.logger.ConsoleLogger
servicetest.logger.FileLogger
Stosowanie
Następująca main metoda zapisuje komunikat do wszystkich dostępnych rejestratorów. Rejestratory są tworzone za pomocą ServiceLoader .
public static void main(String[] args) throws Exception {
final String message = "Hello World!";
// get ServiceLoader for Logger
ServiceLoader<Logger> loader = ServiceLoader.load(servicetest.Logger.class);
// iterate through instances of available loggers, writing the message to each one
Iterator<Logger> iterator = loader.iterator();
while (iterator.hasNext()) {
try (Logger logger = iterator.next()) {
logger.log(message);
}
}
}
Prosty przykład programu ServiceLoader
ServiceLoader jest prostym i łatwym w użyciu wbudowanym mechanizmem do dynamicznego ładowania implementacji interfejsu. Za pomocą modułu ładującego usługi - zapewniającego środki do tworzenia instancji (ale nie okablowania) - w Java SE można zbudować prosty mechanizm wstrzykiwania zależności. Dzięki interfejsowi ServiceLoader separacja implementacji staje się naturalna, a programy można wygodnie rozszerzać. W rzeczywistości wiele Java API jest implementowanych w oparciu o ServiceLoader
Podstawowe pojęcia to
- Działanie na interfejsach usług
- Uzyskiwanie implementacji usługi przez
ServiceLoader - Zapewnienie wdrożenia usług
Zacznijmy od interfejsu i accounting-api.jar go do słoika o nazwie na przykład accounting-api.jar
package example;
public interface AccountingService {
long getBalance();
}
Teraz zapewniamy implementację tej usługi w słoju o nazwie accounting-impl.jar , zawierającym implementację usługi
package example.impl;
import example.AccountingService;
public interface DefaultAccountingService implements AccouningService {
public long getBalance() {
return balanceFromDB();
}
private long balanceFromDB(){
...
}
}
ponadto, plik accounting-impl.jar zawiera plik deklarujący, że ten jar zapewnia implementację usługi AccountingService . Plik musi mieć ścieżkę rozpoczynającą się od META-INF/services/ i musi mieć taką samą nazwę jak pełna nazwa interfejsu:
-
META-INF/services/example.AccountingService
Treść pliku to pełna nazwa nazwy implementacji:
example.impl.DefaultAccountingService
Biorąc pod uwagę, że oba słoiki znajdują się w ścieżce klas programu, który zużywa usługę AccountingService , wystąpienie usługi można uzyskać za pomocą ServiceLauncher
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
AccountingService service = loader.next();
long balance = service.getBalance();
Ponieważ ServiceLoader jest Iterable , obsługuje wielu dostawców implementacji, gdzie program może wybierać spośród:
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
//...
}
Zauważ, że podczas wywoływania next() zawsze zostanie utworzone nowe wystąpienie. Jeśli chcesz ponownie użyć instancji, musisz użyć metody iterator() ServiceLoader lub pętli for-each, jak pokazano powyżej.