サーチ…


備考

ServiceLoaderを使用して、 .jarファイルにパックされたファイルで指定された、指定された型(=サービス)を拡張するクラスのインスタンスを取得できます。拡張/実装されたサービスは多くの場合インタフェースですが、これは必須ではありません。

拡張/実装クラスは、 ServiceLoaderがそれらをインスタンス化するためのゼロ引数コンストラクタを提供する必要があります。

ServiceLoaderによって検出されるには、実装されたサービスの完全修飾型名の名前を持つテキストファイルを、jarファイルのMETA-INF/servicesディレクトリ内に格納する必要があります。このファイルには、1行にサービスを実装するクラスの完全修飾名が1つ含まれています。

ロガーサービス

次の例は、 ServiceLoaderを介してログを記録するためにクラスをインスタンス化する方法を示しています。

サービス

package servicetest;

import java.io.IOException;

public interface Logger extends AutoCloseable {
    
    void log(String message) throws IOException;
}

サービスの実装

次の実装は、単にメッセージをSystem.err書き込みます

package servicetest.logger;

import servicetest.Logger;

public class ConsoleLogger implements Logger {

    @Override
    public void log(String message) {
        System.err.println(message);
    }

    @Override
    public void close() {
    }

}

次の実装では、メッセージをテキストファイルに書き込みます。

package servicetest.logger;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import servicetest.Logger;

public class FileLogger implements Logger {

    private final BufferedWriter writer;

    public FileLogger() throws IOException {
        writer = new BufferedWriter(new FileWriter("log.txt"));
    }

    @Override
    public void log(String message) throws IOException {
        writer.append(message);
        writer.newLine();
    }

    @Override
    public void close() throws IOException {
        writer.close();
    }

}

META-INF / services / servicetest.Logger

META-INF/services/servicetest.Loggerファイルには、 Logger実装の名前がリストされています。

servicetest.logger.ConsoleLogger
servicetest.logger.FileLogger

使用法

次のmainメソッドは、利用可能なすべてのロガーにメッセージを書き込みます。ロガーはServiceLoaderを使用してインスタンス化されます。

public static void main(String[] args) throws Exception {
    final String message = "Hello World!";

    // get ServiceLoader for Logger
    ServiceLoader<Logger> loader = ServiceLoader.load(servicetest.Logger.class);

    // iterate through instances of available loggers, writing the message to each one
    Iterator<Logger> iterator = loader.iterator();
    while (iterator.hasNext()) {
        try (Logger logger = iterator.next()) {
            logger.log(message);
        }
    }
}

単純なServiceLoaderの例

ServiceLoaderは、インターフェイス実装の動的ロードのためのシンプルで使いやすい組み込みのメカニズムです。インスタント化のための手段を提供するサービスローダー(ただし配線はありません)では、Java SEに単純な依存性注入機構を組み込むことができます。 ServiceLoaderインターフェースと実装の分離は自然になり、プログラムは便利に拡張できます。実際、多くのJava APIはServiceLoaderに基づいています

基本的なコンセプトは

  • サービスのインターフェイス上での操作
  • ServiceLoaderを使用してサービスの実装を取得する
  • サービスの実装を提供する

インタフェースから始まり、例えばaccounting-api.jarという名前のjar accounting-api.jar入れてみましょう。

package example;

public interface AccountingService {

  long getBalance();
}

次に、 accounting-impl.jarという名前のjar accounting-impl.jarにサービスの実装を提供します。これにはサービスの実装が含まれています

package example.impl;
import example.AccountingService;

public interface DefaultAccountingService implements AccouningService {

  public long getBalance() {
    return balanceFromDB();
  }

  private long balanceFromDB(){
    ...
  }
}

さらに、 accounting-impl.jarは、このjarがAccountingService実装を提供することを宣言するファイルが含まれています。ファイルは、 META-INF/services/始まるパスを持つ必要があり、 完全修飾名と同じ名前を持つ必要があります。

  • META-INF/services/example.AccountingService

ファイルの内容は、実装の完全修飾名です。

example.impl.DefaultAccountingService

両方のjarがプログラムのクラスパスにあると仮定すると、それはAccountingServiceを消費し、サービスのインスタンスはServiceLauncherを使用して取得できます

ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
AccountingService service = loader.next();
long balance = service.getBalance();

ServiceLoaderIterable 、複数の実装プロバイダがサポートされています。プログラムの選択肢は次のとおりです。

ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
   //...
}

next()を呼び出すと、新しいインスタンスが常に作成されることに注意してください。インスタンスを再利用する場合は、上記のようにServiceLoaderまたはfor-eachループのiterator()メソッドを使用する必要がありiterator()



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow