Интерфейс поставщика услуг Java

Интерфейс поставщика услуг Java

1. обзор

Java 6 представила функцию для обнаружения и загрузки реализаций, соответствующих данному интерфейсу: интерфейс поставщика услуг (SPI).

В этом руководстве мы познакомимся с компонентами Java SPI и покажем, как мы можем применить его в практических случаях использования.

2. Термины и определения Java SPI

Java SPI определяет четыре основных компонента

2.1. обслуживание

Хорошо известный набор программных интерфейсов и классов, обеспечивающих доступ к некоторым конкретным функциональным возможностям или функциям приложения.

2.2. Интерфейс поставщика услуг

Интерфейс или абстрактный класс, который действует как прокси или конечная точка для сервиса.

Если сервис представляет собой один интерфейс, то он совпадает с интерфейсом поставщика услуг.

Служба и SPI вместе известны в экосистеме Java как API.

2.3. Поставщик услуг

Конкретная реализация SPI. Поставщик услуг содержит один или несколько конкретных классов, которые реализуют или расширяют тип службы.

Поставщик услуг настраивается и идентифицируется с помощью файла конфигурации поставщика, который мы помещаем в каталог ресурсовMETA-INF/services. Имя файла - это полное имя SPI, а его содержимое - полное имя реализации SPI.

Поставщик услуг устанавливается в виде расширений, файла jar, который мы помещаем в путь к классам приложения, в путь к расширениям java или в определенный пользователем путь к классам.

2.4. ServiceLoader

В основе SPI лежит классServiceLoader. Это играет роль обнаружения и загрузки ленивых реализаций. Он использует контекстный путь к классам, чтобы найти реализации провайдеров и поместить их во внутренний кеш.

3. Примеры SPI в экосистеме Java

Java предоставляет множество SPI, вот несколько примеров интерфейса поставщика услуг и предоставляемого сервиса:

  • CurrencyNameProvider: предоставляет локализованные символы валюты для классаCurrency.

  • LocaleNameProvider: предоставляет локализованные имена для классаLocale.

  • TimeZoneNameProvider: предоставляет локализованные имена часовых поясов для классаTimeZone.

  • DateFormatProvider: предоставляет форматы даты и времени для указанной локали.

  • NumberFormatProvider: предоставляет денежные, целые и процентные значения для классаNumberFormat.

  • Driver:, начиная с версии 4.0, JDBC API поддерживает шаблон SPI. В более старых версиях для загрузки драйверов используется методClass.forName().

  • PersistenceProvider: обеспечивает реализацию JPA API.

  • JsonProvider: предоставляет объекты обработки JSON.

  • JsonbProvider: предоставляет объекты привязки JSON.

  • Extention: предоставляет расширения для контейнера CDI.

  • ConfigSourceProvider: предоставляет источник для получения свойств конфигурации.

4. Витрина: приложение для курсов обмена валют

Теперь, когда мы понимаем основы, давайте опишем шаги, которые необходимо выполнить для настройки приложения обменного курса.

Чтобы выделить эти шаги, нам нужно использовать как минимум три проекта:exchange-rate-api,exchange-rate-impl, иexchange-rate-app..

In sub-section 4.1., we’ll cover the Service, the SPI, and the ServiceLoader через модульexchange-rate-api,, затем в подразделе 4.2. мы реализуем нашservice *provider* в модулеthe exchange-rate-impl, и, наконец, мы соберем все вместе в подразделе 4.3 через модульexchange-rate-app.

Фактически, мы можем предоставить столько модулей, сколько нам нужно дляserviceprovider, и сделать их доступными в пути к классам модуляexchange-rate-app.

4.1. Создание нашего API

Начнем с создания проекта maven под названиемexchange-rate-api. Хорошей практикой является то, что имя заканчивается терминомapi, но мы можем называть это как угодно.

Затем мы создаем модельный класс для представления курсов валют:

package com.example.rate.api;

public class Quote {
    private String currency;
    private LocalDate date;
    ...
}

И затем мы определяем нашService для получения котировок, создав интерфейсQuoteManager:

package com.example.rate.api

public interface QuoteManager {
    List getQuotes(String baseCurrency, LocalDate date);
}

Затем мы создаемSPI для нашей службы:

package com.example.rate.spi;

public interface ExchangeRateProvider {
    QuoteManager create();
}

И наконец, нам нужно создать служебный классExchangeRate.java, который может использоваться клиентским кодом. Этот класс делегируетServiceLoader.

Сначала мы вызываем статический фабричный методload(), чтобы получить экземплярServiceLoader:

ServiceLoader loader = ServiceLoader .load(ExchangeRateProvider.class);

Затем мы вызываем методiterate() для поиска и получения всех доступных реализаций.

Iterator = loader.iterator();

Результат поиска кэшируется, поэтому мы можем вызвать методServiceLoader.reload(), чтобы обнаружить недавно установленные реализации:

Iterator = loader.reload();

А вот и наш служебный класс:

public class ExchangeRate {

    ServiceLoader loader = ServiceLoader
      .load(ExchangeRateProvider.class);

    public Iterator providers(boolean refresh) {
        if (refresh) {
            loader.reload();
        }
        return loader.iterator();
    }
}

Теперь, когда у нас есть служба для получения всех установленных реализаций, мы можем использовать их все в нашем клиентском коде для расширения нашего приложения или только одну, выбрав предпочтительную реализацию.

Следует отметить, что этот служебный класс не обязательно должен быть частью проектаapi. Код клиента может выбрать вызовServiceLoader methods itself.

4.2. Создание поставщика услуг

Теперь давайте создадим проект Maven с именемexchange-rate-impl и добавим зависимость API кpom.xml:


    com.example
    exchange-rate-api
    1.0.0-SNAPSHOT

Затем мы создаем класс, который реализует наш SPI:

public class YahooFinanceExchangeRateProvider
  implements ExchangeRateProvider {

    @Override
    public QuoteManager create() {
        return new YahooQuoteManagerImpl();
    }
}

А вот реализация интерфейсаQuoteManager:

public class YahooQuoteManagerImpl implements QuoteManager {

    @Override
    public List getQuotes(String baseCurrency, LocalDate date) {
        // fetch from Yahoo API
    }
}

Чтобы быть обнаруженным, мы создаем файл конфигурации провайдера:

META-INF/services/com.example.rate.spi.ExchangeRateProvider

Содержимое файла - это полное имя класса реализации SPI:

com.example.rate.impl.YahooFinanceExchangeRateProvider

4.3. Положить его вместе

Наконец, давайте создадим клиентский проект под названиемexchange-rate-app и добавим зависимость обменного курса-api в путь к классам:


    com.example
    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.example.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.example.rate.app.MainApp

Мы видим, что наш провайдер загружен.

5. Заключение

Теперь, когда мы исследовали механизм Java SPI с помощью четко определенных шагов, должно быть понятно, как использовать Java SPI для создания легко расширяемых или заменяемых модулей.

Хотя в нашем примере использовалась служба обменных курсов Yahoo, чтобы продемонстрировать возможности подключения к другим существующим внешним API, производственным системам не нужно полагаться на сторонние API для создания хороших приложений SPI.

Код, как обычно, можно найтиover on Github.