1概要

Java 6では、特定のインタフェースに一致する実装を検出してロードするための機能、Service Provider Interface(SPI)が導入されました。

このチュートリアルでは、Java SPIのコンポーネントを紹介し、それを実際のユースケースに適用する方法を説明します。


2 Java SPI

の用語と定義

Java SPIは4つの主要コンポーネントを定義します

2.1. サービス

特定のアプリケーション機能または機能へのアクセスを提供する、よく知られたプログラミングインタフェースとクラスのセット。

2.2. サービスプロバイダインタフェース

サービスへのプロキシまたはエンドポイントとして機能するインタフェースまたは抽象クラス。

サービスが1つのインタフェースの場合、それはサービスプロバイダのインタフェースと同じです。

サービスとSPIは、Java EcosystemではAPIとしてよく知られています。

2.3. サービス提供者

SPIの特定の実装サービスプロバイダは、サービスタイプを実装または拡張する1つ以上の具象クラスを含みます。

サービスプロバイダは、リソースディレクトリ

META-INF/services

に配置したプロバイダ設定ファイルを通して設定および識別されます。ファイル名はSPIの完全修飾名であり、その内容はSPI実装の完全修飾名です。

サービスプロバイダは、拡張機能、アプリケーションクラスパスに配置するjarファイル、Java拡張機能クラスパス、またはユーザー定義クラスパスの形式でインストールされます。

2.4. ServiceLoader

SPIの中心にあるのはhttps://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html[

ServiceLoader

]クラスです。これには、実装を発見して遅延的にロードする役割があります。コンテキストクラスパスを使用してプロバイダの実装を見つけ、それらを内部キャッシュに格納します。


3 Java Ecosystem

のSPIサンプル

Javaは多くのSPIを提供しています。サービスプロバイダインタフェースとそれが提供するサービスのサンプルをいくつか示します。


Currency

クラスのローカライズされた通貨記号を提供します。


Locale

クラスのローカライズされた名前を提供します。


TimeZone

クラスのローカライズされたタイムゾーン名を提供します。

指定されたロケールの日付と時刻の形式を提供します。


NumberFormat

クラスの通貨、整数およびパーセント値を提供します。

バージョン4.0以降、JDBC APIはSPIパターンをサポートします。古いバージョンではhttps://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#forName-java.lang.String-[

Class.forName()

]メソッドを使用してドライバをロードします。 。

JPA APIの実装を提供します。

JSON処理オブジェクトを提供します。

JSONバインディングオブジェクトを提供します。

CDIコンテナの拡張機能を提供します。

設定プロパティを取得するためのソースを提供します。


4ショーケース:通貨為替レートアプリケーション

基本を理解したので、次に為替レートの適用を設定するために必要なステップを説明しましょう。

これらのステップを強調するには、少なくとも3つのプロジェクトを使用する必要があります。


交換率のapi、交換率の暗黙、

および交換率の適用__

  • サブセクション4.1では、モジュール

    exchange-rate-apiを通して

    Service



    SPI

    、および

    ServiceLoader


    について説明し、次にサブセクション4.2で説明します。私たちはexchange-rate-impl


    モジュールに

    service provider **

    を実装し、最後に、モジュール

    exchange-rate-app

    を介して4.3項ですべてをまとめます。

実際、

service


provider

に必要な数のモジュールを提供し、それらをモジュール

exchange-rate-app.

のクラスパスで使用可能にすることができます。


4.1. APIを構築する

まずは

exchange-rate-api

というすばらしいプロジェクトを作成します。名前が

api

という用語で終わるのは良い習慣ですが、私たちはそれを何でも呼ぶことができます。

次に、レート通貨を表すためのモデルクラスを作成します。

package com.baeldung.rate.api;

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

そして、

QuoteManagerというインタフェースを作成して、引用符を取得するための

Service__を定義します。

package com.baeldung.rate.api

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

次に、サービス用の

SPI

を作成します。

package com.baeldung.rate.spi;

public interface ExchangeRateProvider {
    QuoteManager create();
}

そして最後に、クライアントコードで使用できるユーティリティクラス

ExchangeRate.java

を作成する必要があります。このクラスは

ServiceLoader

に委任します。

まず、静的なファクトリメソッド

load()

を呼び出して、__ServiceLoaderのインスタンスを取得します。

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

そして、

iterate()

メソッドを呼び出して、利用可能なすべての実装を検索および取得します。

Iterator<ExchangeRateProvider> = loader.iterator();

検索結果はキャッシュされるので、新しくインストールされた実装を見つけるために

ServiceLoader.reload()

メソッドを呼び出すことができます。

Iterator<ExchangeRateProvider> = loader.reload();

そして、これが私たちのユーティリティクラスです。

public class ExchangeRate {

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

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

インストールされたすべての実装を取得するためのサービスが完成したので、クライアントコードでそれらすべてを使用してアプリケーションを拡張することも、優先実装を選択して1つだけ実装することもできます。

  • このユーティリティクラスは

    api

    プロジェクトの一部である必要はないことに注意してください。クライアントコードは、__ServiceLoaderメソッド自体を呼び出すことを選択できます。


4.2. サービスプロバイダの構築

それでは、

exchange-rate-impl

という名前のMavenプロジェクトを作成して、__pom.xmlにAPIの依存関係を追加します。

<dependency>
    <groupId>com.baeldung</groupId>
    <artifactId>exchange-rate-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

それから、SPIを実装するクラスを作成します。

public class YahooFinanceExchangeRateProvider
  implements ExchangeRateProvider {

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

そして、ここで

QuoteManager

インターフェースの実装です。

public class YahooQuoteManagerImpl implements QuoteManager {

    @Override
    public List<Quote> getQuotes(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をクラスパスに追加します。

<dependency>
    <groupId>com.baeldung</groupId>
    <artifactId>exchange-rate-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

この時点で、アプリケーションから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にプラグインすることの力を示しましたが、プロダクションシステムは優れたSPIアプリケーションを作成するためにサードパーティのAPIに頼る必要はありません。

いつものように、コードはhttps://github.com/eugenp/tutorials/tree/master/java-spi[over on Github]にあります。