Java Language
ServiceLoader
Recherche…
Remarques
ServiceLoader
peut être utilisé pour obtenir des instances de classes étendant un type donné (= service) spécifié dans un fichier contenu dans un fichier .jar
. Le service étendu / implémenté est souvent une interface, mais ce n'est pas obligatoire.
Les classes d'extension / implémentation doivent fournir un constructeur à argument nul pour que ServiceLoader
les instancie.
Pour être découvert par ServiceLoader
un fichier texte portant le nom complet du type de nom du service implémenté doit être stocké dans le META-INF/services
du fichier jar. Ce fichier contient un nom qualifié complet d'une classe implémentant le service par ligne.
Service d'enregistrement
L'exemple suivant montre comment instancier une classe pour la journalisation via ServiceLoader
.
Un service
package servicetest;
import java.io.IOException;
public interface Logger extends AutoCloseable {
void log(String message) throws IOException;
}
Implémentations du service
L'implémentation suivante écrit simplement le message sur 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() {
}
}
L'implémentation suivante écrit les messages dans un fichier texte:
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
Le fichier META-INF/services/servicetest.Logger
répertorie les noms des implémentations Logger
.
servicetest.logger.ConsoleLogger
servicetest.logger.FileLogger
Usage
La méthode main
suivante écrit un message sur tous les enregistreurs disponibles. Les enregistreurs sont instanciés à l'aide de 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);
}
}
}
Exemple simple de ServiceLoader
Le ServiceLoader est un mécanisme intégré simple et facile à utiliser pour le chargement dynamique des implémentations d'interface. Avec le chargeur de service - fournissant des moyens pour l’instauration (mais pas le câblage) - un simple mécanisme d’injection de dépendance peut être intégré à Java SE. Avec l’interface ServiceLoader, la séparation de l’implémentation devient naturelle et les programmes peuvent être facilement étendus. En fait, de nombreuses API Java sont implémentées sur la base du ServiceLoader
Les concepts de base sont
- Fonctionnant sur des interfaces de services
- Obtention des implémentations du service via
ServiceLoader
- Mise en place de services
Commençons par l'interface et placez-la dans un pot nommé par exemple accounting-api.jar
package example;
public interface AccountingService {
long getBalance();
}
Maintenant, nous fournissons une implémentation de ce service dans un jar nommé accounting-impl.jar
, contenant une implémentation du service
package example.impl;
import example.AccountingService;
public interface DefaultAccountingService implements AccouningService {
public long getBalance() {
return balanceFromDB();
}
private long balanceFromDB(){
...
}
}
De plus, le fichier accounting-impl.jar
contient un fichier déclarant que ce fichier jar fournit une implémentation de AccountingService
. Le fichier doit avoir un chemin commençant par META-INF/services/
et doit avoir le même nom que le nom complet de l'interface:
-
META-INF/services/example.AccountingService
Le contenu du fichier est le nom entièrement qualifié de l'implémentation:
example.impl.DefaultAccountingService
Étant donné que les deux jars se trouvent dans le chemin de classe du programme, qui utilise AccountingService
, une instance du service peut être obtenue à l'aide de ServiceLauncher.
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
AccountingService service = loader.next();
long balance = service.getBalance();
Comme ServiceLoader
est une Iterable
, il prend en charge plusieurs fournisseurs d’implémentation, parmi lesquels le programme peut choisir:
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
//...
}
Notez que lors de l'appel de next()
une nouvelle instance sera toujours créée. Si vous souhaitez réutiliser une instance, vous devez utiliser la méthode iterator()
du ServiceLoader ou la boucle for-each, comme indiqué ci-dessus.