Java Language
ServiceLoader
Sök…
Anmärkningar
ServiceLoader
kan användas för att få instanser av klasser som förlänger en viss typ (= service) som anges i en fil som är packad i en .jar
fil. Tjänsten som utökas / implementeras är ofta ett gränssnitt, men detta krävs inte.
Utöknings- / implementeringsklasserna måste tillhandahålla en nollargumentkonstruktör för ServiceLoader
att instansera dem.
För att upptäckas av ServiceLoader
en textfil med namnet på det fullständiga namnet på den implementerade tjänsten lagras i META-INF/services
i burken. Den här filen innehåller ett helt kvalificerat namn på en klass som implementerar tjänsten per rad.
Logger Service
Följande exempel visar hur man initierar en klass för loggning via ServiceLoader
.
Service
package servicetest;
import java.io.IOException;
public interface Logger extends AutoCloseable {
void log(String message) throws IOException;
}
Implementering av tjänsten
Följande implementering skriver helt enkelt meddelandet till 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() {
}
}
Följande implementering skriver meddelandena till en textfil:
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 / tjänster / servicetest.Logger
META-INF/services/servicetest.Logger
filen visar namnen på Logger
implementeringarna.
servicetest.logger.ConsoleLogger
servicetest.logger.FileLogger
Användande
Följande main
metoden skriver ett meddelande till alla tillgängliga loggers. Loggarna instanseras med 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);
}
}
}
Enkelt ServiceLoader-exempel
ServiceLoader är en enkel och lättanvänd inbyggd mekanism för dynamisk lastning av gränssnittimplementeringar. Med servicelastaren - som ger medel för omedelbar inställning (men inte kablarna) - kan en enkel injektionsmekanism byggas i Java SE. Med ServiceLoader-gränssnittet och implementeringsseparationen blir naturlig och program kan bekvämt förlängas. Faktiskt implementeras en hel del Java API baserat på ServiceLoader
De grundläggande begreppen är
- Opererar på gränssnitt mellan tjänster
- Få implementering (er) av tjänsten via
ServiceLoader
- Tillhandahålla implementering av servics
Låt oss börja med gränssnittet och lägga det i en burk, benämnd till exempel accounting-api.jar
package example;
public interface AccountingService {
long getBalance();
}
Nu tillhandahåller vi en implementering av den tjänsten i en burk med namnet accounting-impl.jar
, som innehåller en implementering av tjänsten
package example.impl;
import example.AccountingService;
public interface DefaultAccountingService implements AccouningService {
public long getBalance() {
return balanceFromDB();
}
private long balanceFromDB(){
...
}
}
vidare innehåller accounting-impl.jar
en fil som förklarar att denna burk tillhandahåller en implementering av AccountingService
. Filen måste ha en sökväg som börjar med META-INF/services/
och måste ha samma namn som det helt kvalificerade namnet på gränssnittet:
-
META-INF/services/example.AccountingService
Filens innehåll är det fullständiga namnet på implementeringen:
example.impl.DefaultAccountingService
Med tanke på att båda burkarna finns i klassens väg, som förbrukar AccountingService
, kan en instans av tjänsten erhållas med hjälp av ServiceLauncher
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
AccountingService service = loader.next();
long balance = service.getBalance();
Eftersom ServiceLoader
är en Iterable
, stöder den flera implementeringsleverantörer, där programmet kan välja mellan:
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
//...
}
Observera att när man anropar next()
en ny instans alltid att skapas. Om du vill återanvända en instans måste du använda iterator()
-metoden för ServiceLoader eller för varje slinga som visas ovan.