Java Language
ServiceLoader
Buscar..
Observaciones
ServiceLoader
se puede usar para obtener instancias de clases que amplían un tipo dado (= servicio) que se especifican en un archivo empaquetado en un archivo .jar
. El servicio que se extiende / implementa a menudo es una interfaz, pero esto no es necesario.
Las clases de extensión / implementación deben proporcionar un constructor de argumento cero para que ServiceLoader
ejemplifique.
Para que ServiceLoader
descubra, debe ServiceLoader
un archivo de texto con el nombre del nombre de tipo completo del servicio implementado dentro del directorio META-INF/services
en el archivo jar. Este archivo contiene un nombre completo de una clase que implementa el servicio por línea.
Servicio de registrador
El siguiente ejemplo muestra cómo crear una instancia de una clase para el registro a través del ServiceLoader
.
Servicio
package servicetest;
import java.io.IOException;
public interface Logger extends AutoCloseable {
void log(String message) throws IOException;
}
Implementaciones del servicio.
La siguiente implementación simplemente escribe el mensaje en 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 siguiente implementación escribe los mensajes en un archivo de texto:
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
El archivo META-INF/services/servicetest.Logger
enumera los nombres de las implementaciones de Logger
.
servicetest.logger.ConsoleLogger
servicetest.logger.FileLogger
Uso
El siguiente método main
escribe un mensaje a todos los registradores disponibles. Los registradores se ServiceLoader
instancias utilizando 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);
}
}
}
Ejemplo simple de ServiceLoader
El ServiceLoader es un mecanismo integrado simple y fácil de usar para la carga dinámica de implementaciones de interfaz. Con el cargador de servicios, que proporciona medios para la creación de instancias (pero no el cableado), se puede construir un mecanismo de inyección de dependencia simple en Java SE. Con la interfaz y la implementación de ServiceLoader, la separación se vuelve natural y los programas se pueden extender convenientemente. En realidad, una gran cantidad de API de Java están implementadas basadas en el ServiceLoader
Los conceptos básicos son
- Operando en interfaces de servicios
- Obtención de implementaciones del servicio a través de
ServiceLoader
- Proporcionando implementación de servicios
Comencemos con la interfaz y la pongamos en un jar, llamado, por ejemplo, accounting-api.jar
package example;
public interface AccountingService {
long getBalance();
}
Ahora proporcionamos una implementación de ese servicio en un jar llamado accounting-impl.jar
, que contiene una implementación del servicio
package example.impl;
import example.AccountingService;
public interface DefaultAccountingService implements AccouningService {
public long getBalance() {
return balanceFromDB();
}
private long balanceFromDB(){
...
}
}
Además, accounting-impl.jar
contiene un archivo que declara que este contenedor proporciona una implementación de AccountingService
. El archivo debe tener una ruta que comience con META-INF/services/
y debe tener el mismo nombre que el nombre completo de la interfaz:
-
META-INF/services/example.AccountingService
El contenido del archivo es el nombre completo de la implementación:
example.impl.DefaultAccountingService
Dado que ambos archivos están en el classpath del programa, que consume el AccountingService
, se puede obtener una instancia del Servicio utilizando el ServiceLauncher
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
AccountingService service = loader.next();
long balance = service.getBalance();
Como el ServiceLoader
es un Iterable
, admite múltiples proveedores de implementación, desde donde el programa puede elegir:
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
//...
}
Tenga en cuenta que al invocar next()
creará una nueva instancia. Si desea reutilizar una instancia, debe utilizar el método iterator iterator()
del ServiceLoader o el bucle for-each como se muestra arriba.