特定の要件によっては、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.