特定の要件によっては、Javaのサービスローダーメカニズムが目的を達成する場合があります。
つまり、開発者は、JAR / WARファイルのMETA-INF/servicesディレクトリ内のファイルにリストすることにより、クラスが他のクラスをサブクラス化する(またはインターフェイスを実装する)ことを明示的に宣言できます。次にjava.util.ServiceLoader、Classオブジェクトを指定すると、そのクラスの宣言されたすべてのサブクラスのインスタンスを生成するクラスを使用して検出できます(またはがClassインターフェースを表す場合は、そのインターフェースを実装するすべてのクラス)。
このアプローチの主な利点は、サブクラスのクラスパス全体を手動でスキャンする必要がないことです。すべての検出ロジックはServiceLoaderクラス内に含まれ、META-INF/servicesディレクトリで明示的に宣言されたクラスのみをロードします(クラスパス上のすべてのクラスではありません)。 。
ただし、いくつかの欠点があります。
- すべてのサブクラスを見つけるのではなく、明示的に宣言されたものだけを見つけます。したがって、本当にすべてのサブクラスを見つける必要がある場合、このアプローチでは不十分な場合があります。
- 開発者は、
META-INF/servicesディレクトリの下でクラスを明示的に宣言する必要があります。これは開発者の追加の負担であり、エラーが発生しやすくなります。
ServiceLoader.iterator()サブクラスのインスタンスではなく、彼らの生成Classオブジェクトを。これにより2つの問題が発生します。
- サブクラスがどのように構築されるかについては何も言われません-インスタンスの作成には引数なしのコンストラクターが使用されます。
- そのため、サブクラスにはデフォルトのコンストラクターが必要か、引数なしのコンストラクターを明示的に宣言する必要があります。
どうやらJava 9はこれらの欠点のいくつか(特に、サブクラスのインスタンス化に関するもの)に対処する予定です。
例
インターフェースを実装するクラスを見つけることに興味があるとしましょうcom.example.Example:
package com.example;
public interface Example {
public String getStr();
}
クラスcom.example.ExampleImplはそのインターフェースを実装します:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
あなたは、クラスExampleImplがテキストを含むExampleファイルMETA-INF/services/com.example.Exampleを作成することによる実装であることを宣言しますcom.example.ExampleImpl。
その後、次のようにExample(のインスタンスを含むExampleImpl)の各実装のインスタンスを取得できます。
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
System.out.println(example.getStr());
}
// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.