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.