Java Language
ServiceLoader
サーチ…
備考
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();
ServiceLoader
はIterable
、複数の実装プロバイダがサポートされています。プログラムの選択肢は次のとおりです。
ServiceLoader<AccountingService> loader = ServiceLoader.load(AccountingService.class)
for(AccountingService service : loader) {
//...
}
next()
を呼び出すと、新しいインスタンスが常に作成されることに注意してください。インスタンスを再利用する場合は、上記のようにServiceLoaderまたはfor-eachループのiterator()
メソッドを使用する必要がありiterator()
。