Java Language
ServiceLoader
Поиск…
замечания
ServiceLoader
может использоваться для получения экземпляров классов, расширяющих данный тип (= служба), которые указаны в файле, упакованном в файл .jar
. Сервис, который расширен / реализован, часто является интерфейсом, но это не требуется.
Расширяющиеся / реализующие классы должны предоставить конструктор нулевого аргумента для ServiceLoader
для их создания.
Чтобы быть обнаруженным ServiceLoader
текстовый файл с именем полного имени типа внедренной службы должен храниться внутри каталога META-INF/services
в файле jar. Этот файл содержит одно полное имя класса, реализующего службу на строку.
Служба регистрации
В следующем примере показано, как создать экземпляр класса для ведения журнала через ServiceLoader
.
обслуживание
package servicetest;
import java.io.IOException;
public interface Logger extends AutoCloseable {
void log(String message) throws IOException;
}
Реализация услуги
Следующая реализация просто записывает сообщение в 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() {
}
}
Следующая реализация записывает сообщения в текстовый файл:
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 / услуги / servicetest.Logger
В файле META-INF/services/servicetest.Logger
перечислены имена реализации Logger
.
servicetest.logger.ConsoleLogger
servicetest.logger.FileLogger
использование
Следующий main
метод записывает сообщение всем доступным регистраторам. Регистраторы создаются с использованием 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);
}
}
}
Простой пример ServiceLoader
ServiceLoader - простой и простой в использовании встроенный механизм динамической загрузки реализаций интерфейса. С помощью сервис-загрузчика - обеспечения средств для создания (но не для проводки) - в Java SE может быть встроен простой механизм впрыска зависимостей. Интерфейс ServiceLoader и разделение реализации становятся естественными, и программы могут быть удобно расширены. На самом деле многие Java API внедрены на основе ServiceLoader
Основные понятия
- Работа на интерфейсах услуг
- Получение реализации (-ов) службы через
ServiceLoader
- Обеспечение внедрения сервисов
Давайте начнем с интерфейса и поместим его в банку, названную, например, accounting-api.jar
package example;
public interface AccountingService {
long getBalance();
}
Теперь мы предоставляем реализацию этой службы в банке с именем accounting-impl.jar
, содержащей реализацию услуги
package example.impl;
import example.AccountingService;
public interface DefaultAccountingService implements AccouningService {
public long getBalance() {
return balanceFromDB();
}
private long balanceFromDB(){
...
}
}
далее, accounting-impl.jar
содержит файл, объявляющий, что эта банка обеспечивает реализацию AccountingService
. Файл должен иметь путь, начинающийся с META-INF/services/
и должен иметь то же имя, что и полное имя интерфейса:
-
META-INF/services/example.AccountingService
Содержимое файла является полностью qualfified имя реализации:
example.impl.DefaultAccountingService
Поскольку обе банки находятся в пути к классам программы, которая потребляет AccountingService
, экземпляр службы может быть получен с помощью ServiceLauncher
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
AccountingService service = loader.next();
long balance = service.getBalance();
Поскольку ServiceLoader
является Iterable
, он поддерживает несколько поставщиков реализации, в которых программа может выбирать:
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
//...
}
Обратите внимание, что при вызове next()
создан новый экземпляр. Если вы хотите повторно использовать экземпляр, вы должны использовать метод iterator()
для ServiceLoader или для каждого цикла, как показано выше.