provides/uses によるサービス
サービスローダーで疎結合にします。
「provides/uses によるサービス」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
サービスによる疎結合
モジュールは、サービスローダーパターンを使うことで、互いの実装に依存せずに連携できます。
あるモジュールはサービスインターフェースをusesすると宣言し、別のモジュールは実装をprovidesすると宣言します。JVMがそれらを結び付けます。
3つの役割
サービスには3つの参加者がいます。
- サービスインターフェース:契約を定義するもので、いずれかのモジュールに置かれる
- プロバイダー:実装を提供するモジュール
- コンシューマー:実行時に実装を読み込むモジュール
サービスインターフェース
まず契約を定義します。これは、プロバイダーとコンシューマーの両方が参照できるようにエクスポートされたパッケージに置く、通常のインターフェースです。
package com.example.spi;
public interface Codec {
String name();
byte[] encode(String input);
}コンシューマーによるusesの宣言
コンシューマーモジュールは、インターフェースをusesすると宣言します。これにより、JPMSに対して、このモジュールがServiceLoaderを通じて実装を検索することを伝えます。
module com.example.app {
requires com.example.spi;
uses com.example.spi.Codec;
}プロバイダーによるprovidesの宣言
プロバイダーモジュールは、provides ... with ...を使って、どのインターフェースを実装するかを宣言し、具象クラスを指定します。
module com.example.base64 {
requires com.example.spi;
provides com.example.spi.Codec
with com.example.base64.Base64Codec;
}プロバイダーの実装
実装クラスは通常のJavaクラスです。publicであり、publicな引数なしコンストラクターまたはpublicなstatic provider()メソッドのいずれかを備えている必要があります。
package com.example.base64;
import com.example.spi.Codec;
import java.util.Base64;
public class Base64Codec implements Codec {
public String name() { return "base64"; }
public byte[] encode(String input) {
return Base64.getEncoder().encode(input.getBytes());
}
}ServiceLoaderによる読み込み
実行時にコンシューマーはServiceLoader.load(Codec.class)を使って利用可能なすべてのプロバイダーを取得し、それらを反復処理します。実装へのコンパイル時リンクは必要ありません。
import com.example.spi.Codec;
import java.util.ServiceLoader;
public class App {
public static void main(String[] args) {
ServiceLoader<Codec> loader = ServiceLoader.load(Codec.class);
for (Codec codec : loader) {
System.out.println("Found codec: " + codec.name());
}
}
}この仕組みが強力な理由
コンシューマーがプロバイダークラスの名前を直接指定することはありません。新しいプロバイダーモジュールをモジュールパスに追加すれば、自動的に検出されます。
これにより、プラグインアーキテクチャと、依存関係をきれいに逆転させた設計が可能になります。
provider()ファクトリ
生成にロジックが必要な場合は、引数なしコンストラクターの代わりにpublicなstatic provider()メソッドを公開します。JPMSがそれを呼び出してインスタンスを取得します。
package com.example.base64;
import com.example.spi.Codec;
public class CodecFactory {
public static Codec provider() {
return new Base64Codec();
}
}複数のプロバイダー
複数のモジュールが、それぞれ同じインターフェースを提供できます。ServiceLoaderはそれらをすべて返すため、コンシューマーは名前、優先度、または機能に基づいて選択できます。
コンシューマーのコードに変更を加えずに、プロバイダーを追加または削除できます。
プロバイダーのストリーム処理
Java 9以降、ServiceLoaderにはstream()が用意されており、インスタンス化する前にフィルタリングできるProviderハンドルを生成します。生成にコストがかかる場合に便利です。
import com.example.spi.Codec;
import java.util.ServiceLoader;
public class App {
public static void main(String[] args) {
Codec chosen = ServiceLoader.load(Codec.class).stream()
.map(ServiceLoader.Provider::get)
.filter(c -> c.name().equals("base64"))
.findFirst()
.orElseThrow();
System.out.println("Using: " + chosen.name());
}
}クイックチェック
それぞれの役割で使うディレクティブを思い出してください。
まとめ
モジュールのサービスパターンを学びました。
- 3つの役割:サービスインターフェース、プロバイダー、コンシューマー
- コンシューマー:
uses、プロバイダー:provides ... with - 実装は実行時に
ServiceLoaderで読み込む - プロバイダーにはpublicな引数なしコンストラクターまたはstaticな
provider()が必要
次は、既存プロジェクトをモジュールへ移行します。
よくある質問
「provides/uses によるサービス」レッスンは無料ですか?
はい。「provides/uses によるサービス」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「provides/uses によるサービス」で何を学びますか?
サービスローダーで疎結合にします。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「provides/uses によるサービス」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- module-info.java
- requires と exports
- provides/uses によるサービス
- モジュールへの移行