web-dev-qa-db-ja.com

実行時にJava注釈をスキャンする

注釈付きクラスのクラスパス全体を検索する最良の方法は何ですか?

ライブラリを作成しているので、ユーザーがクラスに注釈を付けられるようにしたいので、Webアプリケーションの起動時に特定の注釈についてクラスパス全体をスキャンする必要があります。

これを行うライブラリまたはJava機能を知っていますか?

編集:Java EE 5 WebサービスまたはEJBの新しい機能のようなものを考えています。クラスに@WebServiceまたは@EJBの注釈を付けると、システムはロード中にこれらのクラスを検出し、リモートでアクセスできるようにします。

229
Alotor

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider を使用します

API

基本パッケージからクラスパスをスキャンするコンポーネントプロバイダー。次に、除外フィルターと組み込みフィルターを結果のクラスに適用して、候補を見つけます。

ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(<DO_YOU_WANT_TO_USE_DEFALT_FILTER>);

scanner.addIncludeFilter(new AnnotationTypeFilter(<TYPE_YOUR_ANNOTATION_HERE>.class));

for (BeanDefinition bd : scanner.findCandidateComponents(<TYPE_YOUR_BASE_PACKAGE_HERE>))
    System.out.println(bd.getBeanClassName());
196
Arthur Ronald

もう1つの解決策は、 Googleリフレクション です。

簡単なレビュー:

  • Springを使用している場合は、Springソリューションが最適です。それ以外の場合は、大きな依存関係です。
  • ASMを直接使用するのは少し面倒です。
  • Java Assistを直接使用するのも面倒です。
  • Annoventionは超軽量で便利です。 Maven統合はまだありません。
  • Googleのリフレクションは、Googleコレクションを引き込みます。すべてにインデックスを付けてから、非常に高速です。
140
Jonathan

ClassGraph を使用すると、任意の注釈を持つクラスを見つけることができます。また、関心のある他の基準(例:特定のインターフェースを実装するクラス。 (免責事項、私はClassGraphの著者です。)ClassGraphは、メモリ内、クラスパス上のすべてのクラス、またはクラス内のクラスについて、クラスグラフ全体(すべてのクラス、注釈、メソッド、メソッドパラメータ、フィールド)の抽象表現を構築できますホワイトリストに登録されたパッケージで、必要に応じてそのクラスグラフをクエリできます。 ClassGraphは、他のスキャナーよりも クラスパス指定メカニズムとクラスローダー をサポートし、新しいJPMSモジュールシステムともシームレスに動作します。そのため、ClassGraphに基づいてコードを作成すると、コードの移植性が最大限に高まります。 ここのAPIを参照してください

40
Luke Hutchison

本当に軽量(依存関係なし、シンプルなAPI、15 kb jarファイル)と非常に高速なソリューション、 https://github.com/rmuller/infomas-asl にあるannotation-detectorをご覧ください

免責事項:私は著者です。

22
rmuller

Java Pluggable Annotation Processing APIを使用して、コンパイルプロセス中に実行され、すべての注釈付きクラスを収集し、ランタイム用のインデックスファイルを構築する注釈プロセッサを作成できます。

これは、実行時にクラスパスをスキャンする必要がないため、注釈付きのクラス検出を行うための最速の方法です。これは通常、非常に遅い操作です。また、このアプローチは、ランタイムスキャナーで通常サポートされるURLClassLoadersだけでなく、どのクラスローダーでも機能します。

上記のメカニズムは、すでに ClassIndex ライブラリに実装されています。

これを使用するには、カスタム注釈に @ IndexAnnotated メタ注釈を付けます。これにより、コンパイル時にインデックスファイルが作成されます。META-INF/ annotations/com/test/YourCustomAnnotationはすべての注釈付きクラスをリストします。次を実行することにより、実行時にインデックスにアクセスできます。

ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)
17
Sławek

Scannotation を試してください。

クラスパスまたはWebアプリケーションのlibディレクトリで特定の注釈を検索するために使用できます。

6
Wolf
5
Animesh

Springでは、AnnotationUtilsクラスを使用して次のように書くこともできます。すなわち:

Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);

詳細およびすべての異なる方法については、公式ドキュメントを確認してください: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html

4
magiccrafter

ややオフトピックですが、Springも同様のことを行います <context:component-scan> を使用して、おそらくソースコードを学習できますか?

Springは、「ステレオタイプ化された」クラスを自動的に検出する機能を提供します[...]。これらのクラスを自動検出し、対応するBeanを登録するには、[context:component-scan element]を含める必要があります。

3
toolkit

答えるには遅すぎますか。 ClassPathScanningCandidateComponentProvider または Scannotations のようなライブラリを使用することをお勧めします

しかし、誰かがclassLoaderを使って試してみたいと思った後でも、パッケージ内のクラスから注釈を出力するために自分でいくつか書いています。

public class ElementScanner {

public void scanElements(){
    try {
    //Get the package name from configuration file
    String packageName = readConfig();

    //Load the classLoader which loads this class.
    ClassLoader classLoader = getClass().getClassLoader();

    //Change the package structure to directory structure
    String packagePath  = packageName.replace('.', '/');
    URL urls = classLoader.getResource(packagePath);

    //Get all the class files in the specified URL Path.
    File folder = new File(urls.getPath());
    File[] classes = folder.listFiles();

    int size = classes.length;
    List<Class<?>> classList = new ArrayList<Class<?>>();

    for(int i=0;i<size;i++){
        int index = classes[i].getName().indexOf(".");
        String className = classes[i].getName().substring(0, index);
        String classNamePath = packageName+"."+className;
        Class<?> repoClass;
        repoClass = Class.forName(classNamePath);
        Annotation[] annotations = repoClass.getAnnotations();
        for(int j =0;j<annotations.length;j++){
            System.out.println("Annotation in class "+repoClass.getName()+ " is "+annotations[j].annotationType().getName());
        }
        classList.add(repoClass);
    }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

/**
 * Unmarshall the configuration file
 * @return
 */
public String readConfig(){
    try{
        URL url = getClass().getClassLoader().getResource("WEB-INF/config.xml");
        JAXBContext jContext = JAXBContext.newInstance(RepositoryConfig.class);
         Unmarshaller um =  jContext.createUnmarshaller();
         RepositoryConfig rc = (RepositoryConfig) um.unmarshal(new File(url.getFile()));
         return rc.getRepository().getPackageName();
        }catch(Exception e){
            e.printStackTrace();
        }
    return null;

}
}

そして、構成ファイルで、パッケージ名を入れてクラスにアンマーシャルします。

3
yashpandey

クラスローディングは「オンデマンド」アクティビティであるため、Classloader APIには「列挙」メソッドがありません。通常、クラスパスには数千のクラスがあり、その一部のみが必要です(rt.jar今日では48MBだけです!)。

したがって、couldすべてのクラスを列挙しても、これは非常に時間とメモリを消費します。

単純なアプローチは、セットアップファイル(xmlまたはあなたの好みに合ったもの)に関係するクラスをリストすることです。これを自動的に行う場合は、1つのJARまたは1つのクラスディレクトリに制限してください。

2
mfx

役立つかどうかはわかりませんが、Apache commons-discoveryプロジェクトを調べることができます。

検出プロジェクト

2
Ian McLaird

reflections の代わりを探している場合は Panda Utilities-AnnotationsScanner をお勧めします。リフレクションライブラリのソースコードに基づいた、グアバフリー(グアバには〜3MB、パンダユーティリティには〜200kb)のスキャナーです。

また、将来ベースの検索専用です。含まれるソースを複数回スキャンしたり、誰かが現在のクラスパスをスキャンできるAPIを提供したい場合は、AnnotationsScannerProcessがフェッチしたすべてのClassFilesをキャッシュするため、非常に高速です。

AnnotationsScannerの簡単な使用例:

AnnotationsScanner scanner = AnnotationsScanner.createScanner()
        .includeSources(ExampleApplication.class)
        .build();

AnnotationsScannerProcess process = scanner.createWorker()
        .addDefaultProjectFilters("net.dzikoysk")
        .fetch();

Set<Class<?>> classes = process.createSelector()
        .selectTypesAnnotatedWith(AnnotationTest.class);
1
dzikoysk

Google Reflection インターフェースも検出したい場合。

Spring ClassPathScanningCandidateComponentProviderはインターフェイスを検出していません。

1
madhu_karnati

Google Reflections はSpringよりもはるかに速いようです。この違いを解決するこの機能リクエストを見つけました: http://www.opensaga.org/jira/browse/OS-738

開発中はアプリケーションの起動時間が非常に重要であるため、これがReflectionsを使用する理由です。リフレクションは、私のユースケースでも非常に使いやすいようです(インターフェイスのすべての実装者を見つけてください)。

1
Martin Aubele

SpringにはAnnotatedTypeScannerクラスと呼ばれるものがあります。
このクラスは内部的に使用します

ClassPathScanningCandidateComponentProvider

このクラスには、classpathリソースを実際にスキャンするためのコードが含まれています。これは、実行時に使用可能なクラスメタデータを使用して行われます。

このクラスを単純に拡張するか、スキャンに同じクラスを使用できます。以下はコンストラクターの定義です。

/**
     * Creates a new {@link AnnotatedTypeScanner} for the given annotation types.
     * 
     * @param considerInterfaces whether to consider interfaces as well.
     * @param annotationTypes the annotations to scan for.
     */
    public AnnotatedTypeScanner(boolean considerInterfaces, Class<? extends Annotation>... annotationTypes) {

        this.annotationTypess = Arrays.asList(annotationTypes);
        this.considerInterfaces = considerInterfaces;
    }
0
swayamraina

Scalaライブラリが必要な場合は、Sclasnerを使用します。 https://github.com/ngocdaothanh/sclasner

0
Ngoc Dao