モジュール java.base
パッケージ java.util

クラスServiceLoader<S>

java.lang.Object
java.util.ServiceLoader<S>
型パラメータ:
S - このローダーによってロードされるサービスのタイプ
すべての実装されたインタフェース:
Iterable<S>

public final class ServiceLoader<S> extends Object implements Iterable<S>
サービスの実装をロードする機能。

serviceは、ゼロ、1つ、または多数のサービス・プロバイダが存在する、よく知られているインタフェースまたはクラスです。 「サービス・プロバイダ」 (または単にprovider)は、よく知られているインタフェースまたはクラスを実装またはサブクラス化するクラスです。 ServiceLoaderは、アプリケーションの選択時にランタイム環境にデプロイされたサービス・プロバイダを検出してロードするオブジェクトです。 アプリケーション・コードは、サービス・プロバイダを参照するだけで、サービス・プロバイダを参照するものではなく、複数のサービス・プロバイダ(サービスを通じて公開される機能に基づいています)間を選択できると想定され、サービス・プロバイダが存在しない可能性が処理されます。

サービス・ローダーの入手

アプリケーションは、ServiceLoaderの静的なloadメソッドの1つを起動することによって、指定されたサービスのサービス・ローダーを取得します。 アプリケーションがモジュールの場合、そのモジュール宣言にはサービスを指定するusesディレクティブ文が必要です。プロバイダの場所を特定し、確実に実行されるようにします。 また、アプリケーション・モジュールにサービスが含まれていない場合、そのモジュール宣言には、サービスをエクスポートするモジュールを指定するrequiresディレクティブが必要です。 アプリケーション・モジュールでは、サービスのプロバイダを含むモジュールを必要としないことを強くお薦めします。

サービス・ローダーを使用して、iteratorメソッドを使用してサービスのプロバイダを特定し、インスタンス化することができます。 ServiceLoaderは、streamメソッドを定義して、インスタンス化せずに検査およびフィルタリングできるプロバイダのストリームを取得します。

例として、サービスがエンコーダとデコーダを生成するためのメソッドを定義するインタフェースであるcom.example.CodecFactoryであるとします:


     package com.example;
     public interface CodecFactory {
         Encoder getEncoder(String encodingName);
         Decoder getDecoder(String encodingName);
     }
 

次のコードは、CodecFactoryサービスのサービス・ローダーを取得し、そのイテレータ(enhanced-forループによって自動的に作成される)を使用して、配置されているサービス・プロバイダのインスタンスを生成します:


     ServiceLoader<CodecFactory> loader = ServiceLoader.load(CodecFactory.class);
     for (CodecFactory factory : loader) {
         Encoder enc = factory.getEncoder("PNG");
         if (enc != null)
             ... use enc to encode a PNG file
             break;
         }
 

このコードがモジュールにある場合、com.example.CodecFactoryインタフェースを参照するために、モジュール宣言にはインタフェースをエクスポートするモジュールが必要です。 モジュール宣言では、com.example.CodecFactoryの使用も指定します:


     requires com.example.codec.core;
     uses com.example.CodecFactory;
 

サービス・プロバイダをインスタンス化する前に、そのサービス・プロバイダのインスタンスが有用かどうかを判断するために、アプリケーションがサービス・プロバイダを検査することが必要な場合があります。 例えば、 CodecFactoryのサービス・プロバイダは、"PNG"エンコーダを生成することができ、@PNGで注釈を付けることができます。 次のコードは、サービス・ローダーのstreamメソッドを使用して、イテレータがCodecFactoryのインスタンスを生成する方法とは対照的に、Provider<CodecFactory>のインスタンスを生成します:


     ServiceLoader<CodecFactory> loader = ServiceLoader.load(CodecFactory.class);
     Set<CodecFactory> pngFactories = loader
            .stream()                                              // Note a below
            .filter(p -> p.type().isAnnotationPresent(PNG.class))  // Note b
            .map(Provider::get)                                    // Note c
            .collect(Collectors.toSet());
 
  1. Provider<CodecFactory>オブジェクトのストリーム
  2. p.type()Class<CodecFactory>を生成
  3. get()CodecFactoryのインスタンスを生成

サービスの設計

サービスは単一のタイプです。通常、インタフェースまたは抽象クラスです。 具体的なクラスを使用できますが、これはお勧めしません。 型にはアクセシビリティがある可能性があります。 サービスのメソッドは高度にドメイン固有であるため、このAPI仕様ではフォームまたは関数について具体的なアドバイスを行うことはできません。 ただし、一般的なガイドラインは2つあります:

  1. サービスは、サービス・プロバイダがドメイン固有のプロパティやその他の実装品質のファクタを伝えるために必要な数のメソッドを宣言する必要があります。 次に、サービスのサービス・ローダーを取得するアプリケーションは、アプリケーションのベスト・プロバイダを選択するために、サービス・プロバイダの各インスタンスでこれらのメソッドを呼び出すことができます。

  2. サービスは、そのサービス・プロバイダがサービスの直接実装を意図しているのか、"proxy"や"factory"などの間接的なメカニズムであるのかを表現する必要があります。 サービス・プロバイダは、ドメイン固有のオブジェクトがインスタンス化するのに比較的高価な場合は間接的なメカニズムになりがちです。この場合、サービス・プロバイダは、オンデマンドで"real"実装を作成する抽象であるように設計する必要があります。 たとえば、CodecFactoryサービスは、サービス・プロバイダがコーデック自体ではなくコーデックのファクトリであることをその名前で表しています。これは、特定のコーデックを生成するのが高価で複雑な場合があるためです。

サービス・プロバイダの開発

サービス・プロバイダは単一のタイプです。通常、コンクリート・クラスです。 インタフェースまたは抽象クラスは、後述する静的プロバイダ・メソッドを宣言できるため、許可されています。 型はパブリックである必要があり、内部クラスであってはなりません。

サービス・プロバイダとそのサポート・コードは、モジュールで開発され、アプリケーション・モジュールのパスまたはイメージにデプロイされます。 あるいは、サービス・プロバイダとそのサポート・コードをJARファイルとしてパッケージ化し、アプリケーション・クラス・パスにデプロイすることもできます。 モジュール内でサービス・プロバイダを開発する利点は、その実装のすべての詳細を隠すためにプロバイダを完全にカプセル化できることです。

特定のサービスのサービス・ローダーを取得するアプリケーションは、そのサービスのプロバイダがモジュールにデプロイされているのかJARファイルとしてパッケージ化されているのかは無関係です。 アプリケーションは、サービス・プロバイダのロケーションを把握せずに、サービス・ローダー・イテレータを介して、またはサービス・ローダー・ストリーム内のProviderオブジェクトを介してサービス・プロバイダをインスタンス化します。

サービス・プロバイダをモジュールとしてデプロイ

モジュールで開発されるサービス・プロバイダは、モジュール宣言のprovidesディレクティブで指定する必要があります。 provideディレクティブは、サービスとサービス・プロバイダの両方を指定します。これは、サービスのusesディレクティブを持つ別のモジュールがサービスのサービス・ローダーを取得したときに、プロバイダの位置を特定するのに役立ちます。 モジュールがサービス・プロバイダを含むパッケージをエクスポートしないことを強くお勧めします。 providesディレクティブで、別のモジュールのサービス・プロバイダを指定するモジュールのサポートはありません。

モジュール内で開発されたサービス・プロバイダは、インスタンス化された時点を制御できません。これは、アプリケーションの要求に応じて発生するためですが、インスタンス化の方法を制御します:

  • サービス・プロバイダがプロバイダ・メソッドを宣言すると、サービス・ローダーはそのメソッドを呼び出して、サービス・プロバイダのインスタンスを取得します。 プロバイダ・メソッドは、"provider"という名前のパブリック静的メソッドで、仮パラメータおよびサービスのインタフェースまたはクラスに割り当てることができる戻り型はありません。

    この場合、サービス・プロバイダ自体をサービスのインタフェースまたはクラスに割り当てる必要はありません。

  • サービス・プロバイダがプロバイダ・メソッドを宣言しない場合、サービス・プロバイダはプロバイダ・コンストラクタを介して直接インスタンス化されます。 プロバイダ・コンストラクタは、仮パラメータを持たないパブリック・コンストラクタです。

    この場合、サービス・プロバイダはサービスのインタフェースまたはクラスに割り当てることができる必要があります

アプリケーション・モジュールのパスに「自動モジュール」としてデプロイされるサービス・プロバイダには、プロバイダ・コンストラクタが必要です。 この場合、プロバイダ・メソッドはサポートされていません。

たとえば、モジュールで次のディレクティブが指定されているとします:


     provides com.example.CodecFactory with com.example.impl.StandardCodecs,
              com.example.impl.ExtendedCodecsFactory;
 

where

  • com.example.CodecFactoryは、以前の2メソッド・サービスです。
  • com.example.impl.StandardCodecsは、CodecFactoryを実装し、public no-argsコンストラクタを持つパブリック・クラスです。
  • com.example.impl.ExtendedCodecsFactoryは、CodecFactoryを実装していないパブリック・クラスですが、戻り値の型がCodecFactoryの"provider"というpublic static no-argsメソッドを宣言しています。

サービス・ローダーは、そのコンストラクタを介してStandardCodecsをインスタンス化し、そのproviderメソッドを呼び出すことによってExtendedCodecsFactoryをインスタンス化します。 プロバイダ・コンストラクタまたはプロバイダ・メソッドがpublicであるという要件は、クラス(つまり、サービス・プロバイダ)がクラスのパッケージ外にあるエンティティ(つまり、サービス・ローダー)によってインスタンス化されるというインテントを文書化するのに役立ちます。

クラスパスにサービス・プロバイダをデプロイ

クラスパスのJARファイルとしてパッケージ化されたサービス・プロバイダは、「プロバイダ構成ファイル」をリソース・ディレクトリMETA-INF/servicesに配置することによって識別されます。 プロバイダ構成ファイルの名前は、サービスの完全修飾バイナリ名です。 プロバイダ構成ファイルには、1行に1つずつ、サービス・プロバイダの完全修飾バイナリ名のリストが含まれています。

たとえば、サービス・プロバイダcom.example.impl.StandardCodecsがクラスパスのJARファイルにパッケージされているとします。 JARファイルには、次の名前のプロバイダ構成ファイルが含まれます:

META-INF/services/com.example.CodecFactory
次の行を含む:
com.example.impl.StandardCodecs # Standard codecs

プロバイダ構成ファイルは、UTF-8でエンコードする必要があります。 各サービス・プロバイダ名を囲む空白文字とタブ文字は無視されます。 コメント文字は'#' (U+0023 NUMBER SIGN)です。各行で、最初のコメント文字に続くすべての文字は無視されます。 サービス・プロバイダ・クラス名がプロバイダ構成ファイルに複数回リストされている場合、その複製は無視されます。 複数の構成ファイルでサービス・プロバイダ・クラスの名前が指定されている場合、その複製は無視されます。

プロバイダ構成ファイルに記述されているサービス・プロバイダは、プロバイダ構成ファイルと同じJARファイルまたは別のJARファイルにあります。 サービス・プロバイダは、プロバイダ構成ファイルを見つけるために最初に照会されたクラス・ローダーから可視でなければなりません。これは必ずしも最終的にプロバイダ構成ファイルを見つけるクラス・ローダーではありません。

プロバイダ発見のタイミング

サービス・プロバイダは、遅延して、つまり必要に応じてロードされ、インスタンス化されます。 サービス・ローダーは、以前にロードされたプロバイダのキャッシュを維持管理します。 iteratorメソッドを呼び出すたびに、Iteratorが返され、インスタンス化された順序でキャッシュされたすべての要素が最初に生成され、残りのプロバイダが遅延検索されてインスタンス化され、それぞれがキャッシュに順番に追加されます。 同様に、streamメソッドを呼び出すと、前のストリーム操作によってロードされたすべてのプロバイダを最初に処理し、ロードされた順番にStreamが返され、残りのプロバイダが遅延探索されます。 キャッシュはreloadメソッドでクリアされます。

Errors

サービス・ローダーのiteratorを使用すると、サービス・プロバイダの検索、ロードまたはインスタンス化でエラーが発生すると、hasNextおよびnextメソッドはServiceConfigurationErrorで失敗します。 サービス・ローダーのストリームを処理する場合、サービス・プロバイダを検出またはロードするメソッドによってServiceConfigurationErrorがスローされることがあります。

モジュール内でサービス・プロバイダをロードまたはインスタンス化する場合、次の理由により ServiceConfigurationErrorをスローすることができます:

  • サービス・プロバイダをロードできません。
  • サービス・プロバイダはプロバイダ・メソッドを宣言せず、サービスのインタフェース/クラスに割り当てられないか、プロバイダ・コンストラクタを持っていません。
  • サービス・プロバイダは、"provider"という名前のpublic static no-argsメソッドを、サービスのインタフェースまたはクラスに割り当てられない戻り型で宣言します。
  • サービス・プロバイダ・クラス・ファイルには、"provider"という名前のpublic static no-argsメソッドが複数あります。
  • サービス・プロバイダはプロバイダ・メソッドを宣言し、nullを返すか例外をスローして失敗します。
  • サービス・プロバイダはプロバイダ・メソッドを宣言せず、プロバイダ・コンストラクタは例外をスローすることによって失敗します。

プロバイダ構成ファイルを読み込むとき、またはプロバイダ構成ファイルで指定されたプロバイダ・クラスをロードまたはインスタンス化するときは、次の理由で ServiceConfigurationErrorをスローすることができます:

  • プロバイダ構成ファイルの形式が上記のformatに違反しています。
  • IOExceptionがプロバイダ構成ファイルを読み取っているときに発生
  • サービス・プロバイダはロードできません。
  • サービス・プロバイダは、サービスのインタフェースまたはクラスに割り当てることができないか、プロバイダ・コンストラクタを定義しないか、またはインスタンス化できません。

セキュリティ

サービス・ローダーは、イテレータまたはストリーム・メソッドの呼出し元のセキュリティ・コンテキストで常に実行され、サービス・ローダーを作成した呼出し元のセキュリティ・コンテキストによっても制限されます。 信頼できるシステム・コードは通常、このクラス内のメソッドやそれらのメソッドから返されるイテレータのメソッドを、特権付きのセキュリティ・コンテキスト内から呼び出すべきです。

同時実行性

このクラスのインスタンスは、複数のスレッドで並行して使用することはできません。

ヌル処理

特に指定しないかぎり、このクラスのメソッドにnull引数を渡すと、NullPointerExceptionがスローされます。

導入されたバージョン:
1.6