Java Language
ServiceLoader
Zoeken…
Opmerkingen
ServiceLoader
kan worden gebruikt om instanties van klassen te krijgen die een bepaald type (= service) uitbreiden en die zijn gespecificeerd in een bestand dat is verpakt in een .jar
bestand. De service die wordt uitgebreid / geïmplementeerd is vaak een interface, maar dit is niet vereist.
De uitbreidende / implementerende klassen moeten een nul-argumentconstructor bieden voor de ServiceLoader
om ze te instantiëren.
Om te worden ontdekt door de ServiceLoader
een tekstbestand met de naam van de volledig gekwalificeerde typenaam van de geïmplementeerde service worden opgeslagen in de META-INF/services
in het jar-bestand. Dit bestand bevat één volledig gekwalificeerde naam van een klasse die de service per lijn implementeert.
Logger-service
Het volgende voorbeeld laat zien hoe u een klasse kunt instantiëren voor logboekregistratie via de ServiceLoader
.
Onderhoud
package servicetest;
import java.io.IOException;
public interface Logger extends AutoCloseable {
void log(String message) throws IOException;
}
Implementaties van de dienst
De volgende implementatie schrijft eenvoudig het bericht naar 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() {
}
}
De volgende implementatie schrijft de berichten naar een tekstbestand:
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
Het bestand META-INF/services/servicetest.Logger
bevat de namen van de Logger
implementaties.
servicetest.logger.ConsoleLogger
servicetest.logger.FileLogger
Gebruik
De volgende main
methode schrijft een bericht naar alle beschikbare loggers. De loggers worden geïnstantieerd met 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);
}
}
}
Eenvoudig ServiceLoader-voorbeeld
De ServiceLoader is een eenvoudig en gebruiksvriendelijk ingebouwd mechanisme voor het dynamisch laden van interface-implementaties. Met de servicelader - die middelen biedt voor instantatie (maar niet voor de bedrading) - kan een eenvoudig afhankelijkheidsinjectiemechanisme worden gebouwd in Java SE. Met de ServiceLoader-interface en de implementatie wordt de scheiding natuurlijk en kunnen programma's gemakkelijk worden uitgebreid. Eigenlijk zijn veel Java API's gebaseerd op de ServiceLoader
De basisconcepten zijn
- Werkend op interfaces van diensten
- Het verkrijgen van implementatie (s) van de service via
ServiceLoader
- Zorg voor implementatie van servics
Laten we beginnen met de interface en deze in een pot plaatsen, bijvoorbeeld accounting-api.jar
package example;
public interface AccountingService {
long getBalance();
}
Nu bieden we een implementatie van die service in een pot met de naam accounting-impl.jar
, met een implementatie van de service
package example.impl;
import example.AccountingService;
public interface DefaultAccountingService implements AccouningService {
public long getBalance() {
return balanceFromDB();
}
private long balanceFromDB(){
...
}
}
verder bevat de accounting-impl.jar
een bestand dat accounting-impl.jar
dat dit jar een implementatie van AccountingService
. Het bestand moet een pad hebben dat begint met META-INF/services/
en moet dezelfde naam hebben als de volledig gekwalificeerde naam van de interface:
-
META-INF/services/example.AccountingService
De inhoud van het bestand is de volledig gekwalificeerde naam van de implementatie:
example.impl.DefaultAccountingService
Aangezien beide potten zich in het classpath van het programma bevinden, dat de AccountingService
verbruikt, kan een exemplaar van de Service worden verkregen met behulp van de ServiceLauncher
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
AccountingService service = loader.next();
long balance = service.getBalance();
Aangezien de ServiceLoader
een Iterable
, ondersteunt het meerdere implementatieproviders, waar het programma uit kan kiezen:
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
//...
}
Merk op dat bij het aanroepen van next()
een nieuwe instantie wordt gemaakt. Als u een exemplaar opnieuw wilt gebruiken, moet u de iterator()
-methode van de ServiceLoader of de for-each-lus gebruiken zoals hierboven weergegeven.