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

最終更新: 2024-10-01 22:56:33 +0800 +0800
tags: Java | Spi
原文は英語で書かれています。あなたはAI翻訳版を読んでいます。

Java SPIは、サービス契約を1つ以上のプロバイダおよび、それらを選択するコンシューマから分離します。コンシューマはサービス型に依存し、ServiceLoaderを使用するため、アプリケーションが具象クラスに依存することなく、デプロイ時にプロバイダを追加できます。

クラスパスサービス #

モジュール導入前は、プロバイダJARが META-INF/services/<fully.qualified.ServiceName>に実装を記載していました。UTF-8ファイルには、1行につき1つのプロバイダクラス名を記載します。空行と#コメントを使用できます。各プロバイダは、検出に使用されるローダーからアクセス可能であり、publicな引数なしコンストラクタを提供する必要があります。

java
ServiceLoader<Codec> codecs = ServiceLoader.load(Codec.class);
for (Codec codec : codecs) {
    if (codec.supports(mediaType)) return codec;
}

検出とプロバイダのインスタンス化は遅延実行されます。プロバイダの反復処理、ストリーミング、またはProvider.get()の呼び出しによって、プロバイダのロードと初期化が行われる可能性があります。そのため、不正な設定、依存関係の欠落、アクセスできない型、コンストラクタの失敗はServiceConfigurationErrorとして表面化します。プロバイダコードはデプロイ時に供給されるコードとして扱い、コンテナやプラグインシステムでは明示的なクラスローダーを選択し、制御された起動境界で診断情報とともに失敗させてください。

モジュールサービス #

JPMSでは、コンシューマモジュールがuses com.example.Codecを宣言し、プロバイダがprovides com.example.Codec with com.example.JsonCodecを宣言します。モジュール解決では、モジュールシステムの規則に従ってサービスプロバイダが考慮されます。ServiceLoaderのためだけにプロバイダクラスをexportする必要はありません。名前付きモジュールのプロバイダは、サービス宣言の制約に従う限り、publicな引数なしコンストラクタまたはpublicなstaticプロバイダメソッドを持つことができます。ServiceLoader.load(service, loader)は指定されたローダーの可視性に従いますが、モジュール対応のオーバーロードはモジュールレイヤーを起点に動作します。ローダーとレイヤーはサービス境界の一部であり、偶然の実装詳細ではありません。

クラスパス構成は、unnamed moduleとautomatic moduleで引き続きサポートされるため、移行が可能です。解決されたプロバイダ集合をテストせずに両方の仕組みを公開することは避けてください。重複や順序に関する仮定によって、選択結果が移植不能になるためです。

進化の例としてのJDBC #

初期のJDBCドライバ利用では、Class.forName("vendor.Driver")を呼び出して、DriverManagerへのドライバのstatic登録を発生させることがよくありました。JDBC 4ではjava.sql.Driver向けのサービスプロバイダローディングが導入され、互換性のあるドライバJARをMETA-INF/services/java.sql.Driverから検出できるようになりました。現代のアプリケーションでも、使用するドライバと接続プロパティは明示的に構成すべきです。自動検出によって不要になるのはブートストラップ呼び出しであり、データベース認証やバージョン互換性に関する考慮ではありません。

参考資料 #

コメント

GitHub で閲覧・コメントする(投稿にはログインが必要です)