1. Обзор
Java 6 представила функцию для обнаружения и загрузки реализаций, соответствующих заданному интерфейсу: Интерфейс поставщика услуг (SPI).
В этом уроке мы познакомим вас с компонентами Java SPI и покажем, как мы можем применить их на практике.
2. Термины и определения Java SPI
Java API определяет четыре основных компонента
2.1. Обслуживание
Хорошо известный набор программных интерфейсов и классов, которые обеспечивают доступ к некоторым конкретным функциям или функциям приложения.
2.2. Интерфейс поставщика услуг
Интерфейс или абстрактный класс, который действует как прокси-сервер или конечная точка службы.
Если служба представляет собой один интерфейс, то она совпадает с интерфейсом поставщика услуг.
Сервис и SPI вместе хорошо известны в экосистеме Java как API.
2.3. Поставщик услуг
Конкретная реализация SPI. Поставщик услуг содержит один или несколько конкретных классов, реализующих или расширяющих тип службы.
Поставщик услуг настраивается и идентифицируется с помощью файла конфигурации поставщика, который мы помещаем в каталог ресурсов META-INF/services . Имя файла-это полное имя SOI, а его содержимое-полное имя реализации SPI.
Поставщик услуг устанавливается в виде расширений, файла jar, который мы помещаем в путь к классу приложения, путь к классу расширения Java или определяемый пользователем путь к классу.
2.4. Загрузчик услуг
В основе SPI лежит класс ServiceLoader . Это играет роль ленивого обнаружения и загрузки реализаций. Он использует контекстный путь к классам для поиска реализаций поставщиков и размещения их во внутреннем кэше.
3. Примеры SPI в экосистеме Java
Java предоставляет множество SPI. Вот некоторые примеры интерфейса поставщика услуг и услуг, которые он предоставляет:
- CurrencyNameProvider: предоставляет локализованные символы валюты для класса Currency .
- LocaleNameProvider : предоставляет локализованные имена для класса Locale .
- TimeZoneNameProvider: предоставляет локализованные имена часовых поясов для класса Часовой пояс .
- DateFormatProvider : предоставляет форматы даты и времени для указанной локали.
- NumberFormatProvider : предоставляет денежные, целочисленные и процентные значения для класса NumberFormat .
- Драйвер: начиная с версии 4.0, API JDBC поддерживает шаблон SPI. В более старых версиях для загрузки драйверов используется метод Class.forName () .
- Поставщик персистентности: обеспечивает реализацию API JPA.
- JsonProvider: предоставляет объекты обработки JSON.
- Jsonprovider: предоставляет объекты привязки JSON.
- Расширение: предоставляет расширения для контейнера CDI.
- ConfigSourceProvider : предоставляет источник для получения свойств конфигурации.
4. Витрина: Приложение для обмена валют
Теперь, когда мы понимаем основы, давайте опишем шаги, необходимые для настройки приложения обменного курса.
Чтобы выделить эти шаги, нам нужно использовать по крайней мере три проекта: exchange-rate-api , exchange-rate-simply, и |/exchange-rate-app.
В подразделе 4.1. мы рассмотрим Service , SPI и ServiceLoader | через модуль exchange-rate-api, затем в подразделе 4.2. мы реализуем наш service provider в модуле exchange-rate-impl и, наконец, мы соберем все вместе в подразделе 4.3 через модуль exchange-rate-app .
На самом деле, мы можем предоставить столько модулей, сколько нам нужно для se rvice provider , и сделать их доступными в пути к классам модуля exchange-rate-app.
4.1. Создание нашего API
Мы начинаем с создания проекта Maven под названием exchange-rate-api . Это хорошая практика , что имя заканчивается термином api , но мы можем называть его как угодно.
Затем мы создаем класс модели для представления курсов валют:
package com.baeldung.rate.api; public class Quote { private String currency; private LocalDate date; ... }
А затем мы определяем наш Сервис для извлечения котировок, создав интерфейс QuoteManager:
package com.baeldung.rate.api public interface QuoteManager { ListgetQuotes(String baseCurrency, LocalDate date); }
Затем мы создаем SPI для нашего сервиса:
package com.baeldung.rate.spi; public interface ExchangeRateProvider { QuoteManager create(); }
И, наконец, нам нужно создать класс утилиты ExchangeRate.java , который может быть использован клиентским кодом. Этот класс делегирует ServiceLoader .
Сначала мы вызываем статический заводской метод load () , чтобы получить экземпляр ServiceLoader:
ServiceLoaderloader = ServiceLoader .load(ExchangeRateProvider.class);
А затем мы вызываем метод iterate() для поиска и извлечения всех доступных реализаций.
Iterator= loader.iterator();
Результат поиска кэшируется, поэтому мы можем вызвать метод ServiceLoader.reload() для обнаружения новых установленных реализаций:
Iterator= loader.reload();
А вот и наш класс полезности:
public class ExchangeRate { ServiceLoaderloader = ServiceLoader .load(ExchangeRateProvider.class); public Iterator providers(boolean refresh) { if (refresh) { loader.reload(); } return loader.iterator(); } }
Теперь, когда у нас есть служба для получения всех установленных реализаций, мы можем использовать их все в нашем клиентском коде для расширения нашего приложения или только одну, выбрав предпочтительную реализацию.
Обратите внимание, что этот служебный класс не обязательно должен быть частью интерфейс прикладного программирования проект. Клиентский код может выбрать вызов Сами методы ServiceLoader.
4.2. Построение поставщика услуг
Теперь давайте создадим проект Maven с именем exchange-rate-implicate и добавим зависимость API в pom.xml:
com.baeldung exchange-rate-api 1.0.0-SNAPSHOT
Затем мы создаем класс, который реализует наш SPI:
public class YahooFinanceExchangeRateProvider implements ExchangeRateProvider { @Override public QuoteManager create() { return new YahooQuoteManagerImpl(); } }
А вот реализация интерфейса Quote Manager :
public class YahooQuoteManagerImpl implements QuoteManager { @Override public ListgetQuotes(String baseCurrency, LocalDate date) { // fetch from Yahoo API } }
Чтобы быть обнаруженным, мы создаем файл конфигурации поставщика:
META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider
Содержимое файла-это полное имя класса реализации SPI:
com.baeldung.rate.impl.YahooFinanceExchangeRateProvider
4.3. Собрать Все Воедино
Наконец, давайте создадим клиентский проект под названием exchange-rate-app и добавим зависимость exchange-rate-api в путь к классу:
com.baeldung exchange-rate-api 1.0.0-SNAPSHOT
На этом этапе мы можем вызвать SPI из нашего приложения :
ExchangeRate.providers().forEach(provider -> ... );
4.4. Запуск приложения
Теперь давайте сосредоточимся на построении всех наших модулей:
mvn clean package
Затем мы запускаем наше приложение с помощью команды Java без учета поставщика:
java -cp ./exchange-rate-api/target/exchange-rate-api-1.0.0-SNAPSHOT.jar:./exchange-rate-app/target/exchange-rate-app-1.0.0-SNAPSHOT.jar com.baeldung.rate.app.MainApp
Теперь мы включим нашего провайдера в расширение java.ext.dirs и снова запустим приложение:
java -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:./exchange-rate-impl/target:./exchange-rate-impl/target/depends -cp ./exchange-rate-api/target/exchange-rate-api-1.0.0-SNAPSHOT.jar:./exchange-rate-app/target/exchange-rate-app-1.0.0-SNAPSHOT.jar com.baeldung.rate.app.MainApp
Мы видим, что наш провайдер загружен.
5. Заключение
Теперь, когда мы изучили механизм Java SPI с помощью четко определенных шагов, должно быть ясно, как использовать Java SPI для создания легко расширяемых или заменяемых модулей.
Хотя в нашем примере использовался сервис обменного курса Yahoo, чтобы показать возможности подключения к другим существующим внешним API, производственным системам не нужно полагаться на сторонние API для создания отличных приложений SPI.
Код, как обычно, можно найти на Github .