Java Language
ServiceLoader
Ricerca…
Osservazioni
ServiceLoader
può essere utilizzato per ottenere istanze di classi che estendono un determinato tipo (= servizio) specificato in un file contenuto in un file .jar
. Il servizio che viene esteso / implementato è spesso un'interfaccia, ma non è necessario.
Le classi di estensione / implementazione devono fornire un costruttore di argomenti zero per ServiceLoader
per istanziarle.
Per essere scoperto dal ServiceLoader
un file di testo con il nome del nome di tipo completo del servizio implementato deve essere memorizzato all'interno della META-INF/services
nel file jar. Questo file contiene un nome completo di una classe che implementa il servizio per riga.
Servizio logger
L'esempio seguente mostra come istanziare una classe per la registrazione tramite ServiceLoader
.
Servizio
package servicetest;
import java.io.IOException;
public interface Logger extends AutoCloseable {
void log(String message) throws IOException;
}
Implementazioni del servizio
La seguente implementazione scrive semplicemente il messaggio su 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() {
}
}
La seguente implementazione scrive i messaggi in un file di testo:
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
Il file META-INF/services/servicetest.Logger
elenca i nomi delle implementazioni di Logger
.
servicetest.logger.ConsoleLogger
servicetest.logger.FileLogger
uso
Il seguente metodo main
scrive un messaggio a tutti i logger disponibili. I logger vengono istanziati utilizzando 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);
}
}
}
Semplice esempio ServiceLoader
ServiceLoader è un meccanismo integrato semplice e facile da usare per il caricamento dinamico delle implementazioni dell'interfaccia. Con il caricatore di servizio, che fornisce mezzi per l'istanziazione (ma non il cablaggio), è possibile creare un semplice meccanismo di iniezione delle dipendenze in Java SE. Con l'interfaccia ServiceLoader e la separazione dell'implementazione diventa naturale e i programmi possono essere estesi in modo conveniente. In realtà molte API Java sono implementate in base al ServiceLoader
I concetti di base sono
- Operativo su interfacce di servizi
- Ottenere l'implementazione (s) del servizio tramite
ServiceLoader
- Fornire l'implementazione di servizi
Iniziamo dall'interfaccia e la accounting-api.jar
in un vaso, chiamato ad esempio accounting-api.jar
package example;
public interface AccountingService {
long getBalance();
}
Ora forniamo un'implementazione di tale servizio in un jar denominato accounting-impl.jar
, contenente un'implementazione del servizio
package example.impl;
import example.AccountingService;
public interface DefaultAccountingService implements AccouningService {
public long getBalance() {
return balanceFromDB();
}
private long balanceFromDB(){
...
}
}
inoltre, accounting-impl.jar
contiene un file che dichiara che questo jar fornisce un'implementazione di AccountingService
. Il file deve avere un percorso che inizia con META-INF/services/
e deve avere lo stesso nome del nome completo dell'interfaccia:
-
META-INF/services/example.AccountingService
Il contenuto del file è il nome completo dell'implementazione:
example.impl.DefaultAccountingService
Dato che entrambi i jar si trovano nel classpath del programma, che utilizza AccountingService
, è possibile ottenere un'istanza del servizio utilizzando ServiceLauncher
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
AccountingService service = loader.next();
long balance = service.getBalance();
Come il ServiceLoader
è un Iterable
, supporta diversi operatori di attuazione, in cui il programma può scegliere tra:
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
//...
}
Si noti che quando si invoca next()
verrà sempre creata una nuova istanza. Se si desidera riutilizzare un'istanza, è necessario utilizzare il metodo iterator()
del ServiceLoader o del ciclo for-each, come mostrato sopra.