Javaサービスプロバイダーインターフェイス
1. 概要
Java 6では、特定のインターフェースに一致する実装を検出してロードするための機能、サービス・プロバイダー・インターフェース(SPI)が導入されました。
このチュートリアルでは、Java SPIのコンポーネントを紹介し、それを実際のユースケースに適用する方法を示します。
2. JavaSPIの用語と定義
JavaSPIは4つの主要コンポーネントを定義します
2.1. サービス
特定のアプリケーション機能または機能へのアクセスを提供する、よく知られたプログラミングインターフェイスとクラスのセット。
2.2. サービスプロバイダーインターフェイス
サービスのプロキシまたはエンドポイントとして機能するインターフェイスまたは抽象クラス。
サービスが1つのインターフェイスである場合、それはサービスプロバイダーインターフェイスと同じです。
サービスとSPIは、JavaエコシステムではAPIとしてよく知られています。
2.3. サービスプロバイダー
SPIの特定の実装。 サービスプロバイダーには、サービスタイプを実装または拡張する1つ以上の具象クラスが含まれています。
サービスプロバイダーは、リソースディレクトリ META-INF /servicesに配置されたプロバイダー構成ファイルを介して構成および識別されます。 ファイル名はSPIの完全修飾名であり、その内容はSPI実装の完全修飾名です。
サービスプロバイダーは、拡張機能、アプリケーションクラスパス、Java拡張機能クラスパスまたはユーザー定義クラスパスに配置するjarファイルの形式でインストールされます。
2.4. ServiceLoader
SPIの中心には、ServiceLoaderクラスがあります。 これには、実装を怠惰に発見してロードする役割があります。 コンテキストクラスパスを使用してプロバイダーの実装を見つけ、それらを内部キャッシュに配置します。
3. JavaエコシステムのSPIサンプル
Javaは多くのSPIを提供します。 以下に、サービスプロバイダーインターフェイスとそれが提供するサービスのサンプルをいくつか示します。
- CurrencyNameProvider:は、Currencyクラスのローカライズされた通貨記号を提供します。
- LocaleNameProvider:は、Localeクラスのローカライズされた名前を提供します。
- TimeZoneNameProvider:は、TimeZoneクラスのローカライズされたタイムゾーン名を提供します。
- DateFormatProvider:は、指定されたロケールの日付と時刻の形式を提供します。
- NumberFormatProvider:は、 NumberFormat クラスの金額、整数、およびパーセンテージの値を提供します。
- ドライバー:バージョン4.0以降、JDBCAPIはSPIパターンをサポートします。 古いバージョンでは、 Class.forName()メソッドを使用してドライバーをロードします。
- PersistenceProvider:は、JPAAPIの実装を提供します。
- JsonProvider:はJSON処理オブジェクトを提供します。
- JsonbProvider:はJSONバインディングオブジェクトを提供します。
- 拡張機能:はCDIコンテナの拡張機能を提供します。
- ConfigSourceProvider:は、構成プロパティを取得するためのソースを提供します。
4. ショーケース:為替レートアプリケーション
基本を理解したところで、為替レートアプリケーションを設定するために必要な手順を説明しましょう。
これらの手順を強調するには、少なくとも3つのプロジェクトを使用する必要があります: exchange-rate-api 、 exchange-rate-impl、、
サブセクション4.1。では、モジュール exchange-rate-api、を介して、サービス、SPI、およびServiceLoaderについて説明します。次にサブセクション4.2で説明します。 サービスプロバイダーをexchange-rate-implモジュールに実装し、最後に、サブセクション4.3でモジュールexchange-を介してすべてをまとめます。 rate-app。
実際、 se rvice プロバイダーに必要な数のモジュールを提供し、モジュールexchangeのクラスパスで使用できるようにすることができます-レートアプリ。
4.1. APIの構築
まず、exchange-rate-apiというMavenプロジェクトを作成します。 名前の末尾が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プロジェクトを作成し、API依存関係を pom.xml:に追加します。
<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. 結論
明確に定義された手順でJavaSPIメカニズムを検討したので、JavaSPIを使用して簡単に拡張または交換可能なモジュールを作成する方法を理解する必要があります。
この例では、Yahooの為替レートサービスを使用して他の既存の外部APIにプラグインする能力を示しましたが、本番システムは、優れたSPIアプリケーションを作成するためにサードパーティのAPIに依存する必要はありません。
コードは、いつものように、Githubのにあります。