Javaでプログラムによってインターフェースのすべての実装のリストを取得するにはどうすればよいですか?[閉まっている]


81

リフレクションなどでできますか?


いくつかの魔法のEclipseのショートカットでそれを行うことは可能ですか??
Tomasz Waszczyk 2014

4
答えを選ぶことを検討してください。
please_Dont_Bully_Me_SO_Lords 2018

回答:


58

私はしばらく探していましたが、さまざまなアプローチがあるようです。要約は次のとおりです。

  1. 依存関係を追加してもかまわない場合は、リフレクションライブラリが非常に人気があります。次のようになります。

    Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
    Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
    
  2. ServiceLoader(エリクソンの回答による)とそれは次のようになります:

    ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
    for (Pet implClass : loader) {
        System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
    }
    

    これを機能させるにはPet、ServiceProviderInterface(SPI)として定義し、その実装を宣言する必要があることに注意してください。resources/META-INF/servicesその名前でファイルを作成し、その中のexamples.reflections.Petすべての実装を宣言することによってPetそれを行います

    examples.reflections.Dog
    examples.reflections.Cat
    
  3. パッケージレベルの注釈。ここに例があります:

    Package[] packages = Package.getPackages();
    for (Package p : packages) {
        MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class);
        if (annotation != null) {
            Class<?>[]  implementations = annotation.implementationsOfPet();
            for (Class<?> impl : implementations) {
                System.out.println(impl.getSimpleName());
            }
        }
    }
    

    および注釈の定義:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.PACKAGE)
    public @interface MyPackageAnnotation {
        Class<?>[] implementationsOfPet() default {};
    }
    

    また、package-info.javaそのパッケージ内で名前が付けられたファイルでパッケージレベルの注釈を宣言する必要があります。サンプルの内容は次のとおりです。

    @MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
    package examples.reflections;
    

    の呼び出しによって、その時点でClassLoaderに認識されているパッケージのみがロードされることに注意してくださいPackage.getPackages()

さらに、ディレクトリベースの検索を行わない限り、URLClassLoaderに基づく他のアプローチがあり、常にすでにロードされているクラスに制限されます。


3つのアプローチのどれがより効率的で最も速いですか?
carlspring 2016年

@carlspring相対的な効率について絶対的な声明を出すことはできませんが、最初の選択肢(リフレクションライブラリ)は私にとってはかなりうまくいきました。
Ahmad Abdelghany 2016年

JDBCのDriverManagerはどのように機能しますか?同様のことをしていませんか(クラスパス内のドライバーインターフェイスのすべての実装を検索しています)?
AlexSemeniuk19年

1
@AlexSemeniukドキュメントによると、サービスローダー/プロバイダーメカニズム(上記のアプローチ#2)をサポートするようになったと思います。「DriverManagerメソッドgetConnectionおよびgetDriversは、Java StandardEditionサービスプロバイダーメカニズムをサポートするように拡張されました。」docs.oracle.com/javase/8/docs/api/java/sql/DriverManager.html
AhmadAbdelghany19年

1
サービスプロバイダーメカニズムがJava9でモジュールシステムに統合されていることは注目に値します。これにより、パッケージレベルのアノテーションよりもさらに便利になります。つまり、独自のアノテーションタイプを作成する必要がなく、モジュールで実装を宣言できます。 -モジュラーソフトウェアを作成するときにとにかく作成し、実装の有効性に関するコンパイル時のフィードバックを取得する情報。
ホルガー

28

エリクソンが言ったことですが、それでもやりたい場合は、Reflectionsを見てください。彼らのページから:

Reflectionsを使用すると、メタデータに次のクエリを実行できます。

  • あるタイプのすべてのサブタイプを取得する
  • いくつかの注釈で注釈を付けられたすべてのタイプを取得します
  • 一致する注釈パラメータを含む、いくつかの注釈で注釈が付けられたすべてのタイプを取得します
  • いくつかの注釈が付けられたすべてのメソッドを取得する

12
より具体的に:new Reflections("my.package").getSubTypesOf(MyInterface.class)
zapp 2013年

27

一般に、これを行うには費用がかかります。リフレクションを使用するには、クラスをロードする必要があります。クラスパスで使用可能なすべてのクラスをロードする場合は、時間とメモリが必要になるため、お勧めしません。

これを回避したい場合は、リフレクションではなく、より効率的に動作する独自のクラスファイルパーサーを実装する必要があります。バイトコードエンジニアリングライブラリは、このアプローチに役立つ場合があります。

サービスプロバイダのメカニズムは、プラグイン可能なサービスの実装を列挙するための従来の手段であり、Javaの9使用中のプロジェクトジグソーパズル(モジュール)の導入により、より確立されているServiceLoaderJavaで6、またはそれ以前のバージョンでは、独自に実装します。私は別の答えでを提供しまし


4
残念ながら、サービスプロバイダーのメカニズムでは、対象のリストに含まれる可能性のあるクラスを別のファイルにリストする必要がありますが、これは多くの場合実行不可能です。
averasko 2015年

4
インターフェイスを実装するソースコードを記述し、コンパイルしてデプロイすることは可能ですが、実装のFQNにテキストファイルを含めることは不可能ですか?それはめったにありません。ほとんど「頻繁に」。
エリクソン2017年

JDBCのDriverManagerはどのように機能しますか?同様のことをしていませんか(クラスパス内のドライバーインターフェイスのすべての実装を検索しています)?
AlexSemeniuk19年

1
@AlexSemeniukいいえ。上記で説明したサービスローダーメカニズムを使用します。A JDBC 4.0以降のドライバは、でその名前をリストする必要がありますMETA-INF/services/java.sql.Driver
エリクソン

16

Springには、これを実現するための非常に簡単な方法があります。

public interface ITask {
    void doStuff();
}

@Component
public class MyTask implements ITask {
   public void doStuff(){}
}

次に、タイプのリストを自動配線するITaskと、Springがすべての実装をリストに追加します。

@Service
public class TaskService {

    @Autowired
    private List<ITask> tasks;
}

4
正確には、SpringはITask型のすべてのBeanにデータを入力しますが、これはまったく同じではありません。
オリヴィエGérardin

5

特定のインターフェイスを実装するすべてのクラスを一覧表示するための最も堅牢なメカニズムは、現在ClassGraphです。これは、新しいJPMSモジュールシステムを含む、クラスパス仕様メカニズムの可能な限り幅広い配列を処理するためです。(私は著者です。)

try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
        .enableClassInfo().scan()) {
    for (ClassInfo ci : scanResult.getClassesImplementing("x.y.z.SomeInterface")) {
        foundImplementingClass(ci);  // Do something with the ClassInfo object
    }
}

1
ClassGraphライブラリはシャームのように機能します。ありがとう@LukeHutchison
KyryloSemenko20年

4

エリクソンが言ったことは最高です。これが関連する質問と回答のスレッドです-://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html

Apache BCELライブラリを使用すると、クラスをロードせずに読み取ることができます。検証手順をスキップできるはずなので、もっと速くなると思います。クラスローダーを使用してすべてのクラスをロードする際のもう1つの問題は、メモリに大きな影響を与えるだけでなく、おそらく実行したくない静的コードブロックを誤って実行することです。

ApacheBCELライブラリリンク-http //jakarta.apache.org/bcel/


4

ClassGraphそれはかなり簡単です:

の実装を見つけるためのGroovyコードmy.package.MyInterface

@Grab('io.github.classgraph:classgraph:4.6.18')
import io.github.classgraph.*
new ClassGraph().enableClassInfo().scan().withCloseable { scanResult ->
    scanResult.getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.name
}

scan().withCloseable { ... }Groovyで呼び出すか、Javaでtry-with-resourcesを使用する必要があります:github.com/classgraph/classgraph/wiki/…また、クラスの名前を取得するための正しいメソッドであるため、最後の部分はで.nameはなく.className、である必要があります。オブジェクト.getName()からClassInfo
ルークハチソン


2

@ kaybee99の回答の新しいバージョンですが、ユーザーの質問を返します:実装...

Springには、これを実現するための非常に簡単な方法があります。

public interface ITask {
    void doStuff();
    default ITask getImplementation() {
       return this;
    }

}

@Component
public class MyTask implements ITask {
   public void doStuff(){}
}

次に、タイプのリストを自動配線するITaskと、Springがすべての実装をリストに追加します。

@Service
public class TaskService {

    @Autowired(required = false)
    private List<ITask> tasks;

    if ( tasks != null)
    for (ITask<?> taskImpl: tasks) {
        taskImpl.doStuff();
    }   
}

1

また、IDEプラグインを作成している場合(実行しようとしていることが比較的一般的である場合)、IDEは通常、ユーザーコードの現在の状態のクラス階層にアクセスするためのより効率的な方法を提供します。


1

私は同じ問題に遭遇しました。私の解決策は、リフレクションを使用してObjectFactoryクラスのすべてのメソッドを調べ、バインドされたPOJOの1つのインスタンスを返すcreateXXX()メソッドではないメソッドを削除することでした。そのように検出された各クラスは、Class []配列に追加され、JAXBContextインスタンス化呼び出しに渡されます。これはうまく機能し、とにかく必要になろうとしていたObjectFactoryクラスをロードするだけで済みます。ObjectFactoryクラスを維持する必要があるのは、手動で実行されるタスク(私の場合、POJOから始めてschemagenを使用したため)、またはxjcで必要に応じて生成できるタスクだけです。いずれにせよ、それはパフォーマンスが高く、シンプルで、効果的です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.