web-dev-qa-db-ja.com

特定のインターフェースを実装するすべてのクラスを見つける

私は実際に作業を実行するジョブクラスがあり、Quartzスケジューラでトリガーを作成しながらジョブクラスの名前を伝えたり渡す必要があるアプリケーション(Quartzスケジューラ)を開発中です。

APIを使用したいすべての人に拡張ポイントを提供したい(APIの一部として提供するいくつかの一般的なジョブに加えて)。 (マーカー)インターフェースを作成し、そのクラスをスケジューラージョブクラスとして宣言したい場合は、インターフェースを実装(宣言)するだけです。

スケジューラーでトリガーをスケジュールしたいユーザーにそれらを示すことができるように、どのインターフェースが(インターフェースを実装することにより)コントラクトに従っているかをどのように見つけることができるかわかりません。

私の要件は、実行時にクラスをロードするのではなく、ユーザーがクラスとクラス名を選択してスケジューラに渡すことができるように、必要なインターフェイスを実装するクラスのユーザーリストを表示することです。最後にクラスのインスタンスを作成するのはQuartzスケジューラです。

上記の目標を達成する方法を提案できますか、または私がやろうとしていることを達成するためのより良い方法はありますか?

編集

ServiceLoader のドキュメントを調べましたが、サービスを実装するには、META-INFフォルダーに実装クラスの名前でファイルを作成する必要があるようです。私のAPIのユーザーは20種類の実装を望んでいますが、特定のジョブを実行するために各ジョブクラスが作成され、数百のジョブがある可能性があるため、エンドユーザーにとって多くの余分な作業のように思えますクラス。

私の仮定が間違っている場合は私を修正してください。

33
Umesh Awasthi

特定のインターフェイスを実装する作成されたクラスが常に真にシリアライズ可能であることを確認したいという同様のニーズがありました。クラスパス内のすべてのディレクトリをウォークスルーするJavaClassFinderを作成し、関心のあるインターフェイスに割り当て可能なすべてのクラスを見つけました。コードスニペットは次のとおりです。

public <T> List<Class<? extends T>> findAllMatchingTypes(Class<T> toFind) {
    foundClasses = new ArrayList<Class<?>>();
    List<Class<? extends T>> returnedClasses = new ArrayList<Class<? extends T>>();
    this.toFind = toFind;
    walkClassPath();
    for (Class<?> clazz : foundClasses) {
        returnedClasses.add((Class<? extends T>) clazz);
    }
    return returnedClasses;
}

役立つ場合は、コードを共有できてうれしいです。唯一の欠点は、これが.classファイルのみを処理することです。jarを解凍してそこからクラスファイルを読み取る機能を追加しませんでした。 (しかし、それを追加するのは大きなプロジェクトではありません。)

UPDATE:上記のソースコードを確認したところ、標準ユーティリティライブラリの多くのヘルパークラスに依存していることがわかりました。簡単にするために、必要なすべてのコードを圧縮しました。これは JavaClassFinder.Zip からダウンロードできます。これは、Eclipseで直接セットアップされ、必要なコードのどの部分でも使用できます。

プロジェクトには、JavaClassFinderTest.Javaと呼ばれるJUnit3テストがあり、JavaClassFinderクラスの機能と使用法を示します。 Junitテストの実行に必要な唯一の外部依存関係はJunitです。

このユーティリティの基本的な使用法:

    JavaClassFinder classFinder = new JavaClassFinder();
    List<Class<? extends MyTagInterface>> classes = classFinder.findAllMatchingTypes(MyTagInterface.class);

これにより、「MyTagInterface.class」から割り当て可能なクラスパス内のクラスを含むリストが表示されます(たとえば)。お役に立てれば。

33
Sam Goldberg

あなたは答えを見つけることができます こちら

Org.reflectionsの使用を提案できます

リンクの説明をここに入力

Reflections reflections = new Reflections("com.mycompany");    
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);
33
Alex Stybaev

org.reflectionsは、Alex Stybaevが述べた適切なソリューションです。プロパティファイルのクラスを参照する必要はありません。

別のアプローチ(私が取る)は Spring です。これは、アプリケーションにとにかくSpringを使用しているため、追加の依存関係を必要としないためです。

Springで問題を解決するためのヒント(および他のコメントまたは回答の代替案)は、ここにあります。

あなたの質問のコメントでは、HeikkimとpolypielはSpringでそれを解決するための答えを持っている質問にもリンクしています:

2
ChrLipp

おそらくJava SPIメカニズム、 Javadoc を参照)を使用してこれを行うための最良の(標準)方法です。また、素晴らしい機能)は、拡張子を定義するjarがMETA-INF/services/your.fully.qualified.Interface

私が考えることができる他の唯一の方法は、そこにファイルをリストし、クラスファイルをロードし、それらがあなたのインターフェースを実装しているかどうかを見ることができることを願って、すべてのClassLoadersを通過することです-これは素晴らしいことではありませんする。

1
Romain

Find Javaインターフェイスを実装するクラス の答えから、私は http://software.clapper.org/javautil/ :Veryを使用しました高速、非常に便利です。

0
Jayan

Pure Javaでこれを行うには、最も正しい答えがすでに投票されています(2012年4月10日、Sam Goldbergから)

しかし、彼の完全なコードは不必要に複雑です。私は彼のアイデアを使用してClassFinderをプッシュしました: http://icedtea.classpath.org/hg/icedtea-web/file/0527ad4eb2dd/netx/net/sourceforge/jnlp/controlpanel/ClassFinder.Java

注-これは、ServerContainerではなく、スタンドアロンアプリケーション(またはjarおよびdirsで通常のクラスパスを使用するアプリケーション)で動作します。

アプリがウェブサーバーで実行されている場合は、戦争/耳の場所を把握してそこを検索する必要があります。または、親(および他の)ソースを取得している場所を親のclasslaoderに尋ねる必要があります。

2番目の注意-最終的なロケーションクラスをフィルタリングして、netxとicedtea-webのみに一致するようにします。依存関係が検索されないためです。 rt.jarを含める必要がある場合は、rmeove thsoeフィルターを使用してください。または、逆にまったく必要ない場合は、bootclasspathを削除します。

0
judovana