注釈付きクラスのクラスパス全体を検索する最良の方法は何ですか?
ライブラリを作成しているので、ユーザーがクラスに注釈を付けられるようにしたいので、Webアプリケーションの起動時に特定の注釈についてクラスパス全体をスキャンする必要があります。
これを行うライブラリまたはJava機能を知っていますか?
編集:Java EE 5 WebサービスまたはEJBの新しい機能のようなものを考えています。クラスに@WebService
または@EJB
の注釈を付けると、システムはロード中にこれらのクラスを検出し、リモートでアクセスできるようにします。
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());
もう1つの解決策は、 Googleリフレクション です。
簡単なレビュー:
ClassGraph を使用すると、任意の注釈を持つクラスを見つけることができます。また、関心のある他の基準(例:特定のインターフェースを実装するクラス。 (免責事項、私はClassGraphの著者です。)ClassGraphは、メモリ内、クラスパス上のすべてのクラス、またはクラス内のクラスについて、クラスグラフ全体(すべてのクラス、注釈、メソッド、メソッドパラメータ、フィールド)の抽象表現を構築できますホワイトリストに登録されたパッケージで、必要に応じてそのクラスグラフをクエリできます。 ClassGraphは、他のスキャナーよりも クラスパス指定メカニズムとクラスローダー をサポートし、新しいJPMSモジュールシステムともシームレスに動作します。そのため、ClassGraphに基づいてコードを作成すると、コードの移植性が最大限に高まります。 ここのAPIを参照してください
本当に軽量(依存関係なし、シンプルなAPI、15 kb jarファイル)と非常に高速なソリューション、 https://github.com/rmuller/infomas-asl にあるannotation-detector
をご覧ください
免責事項:私は著者です。
Java Pluggable Annotation Processing APIを使用して、コンパイルプロセス中に実行され、すべての注釈付きクラスを収集し、ランタイム用のインデックスファイルを構築する注釈プロセッサを作成できます。
これは、実行時にクラスパスをスキャンする必要がないため、注釈付きのクラス検出を行うための最速の方法です。これは通常、非常に遅い操作です。また、このアプローチは、ランタイムスキャナーで通常サポートされるURLClassLoadersだけでなく、どのクラスローダーでも機能します。
上記のメカニズムは、すでに ClassIndex ライブラリに実装されています。
これを使用するには、カスタム注釈に @ IndexAnnotated メタ注釈を付けます。これにより、コンパイル時にインデックスファイルが作成されます。META-INF/ annotations/com/test/YourCustomAnnotationはすべての注釈付きクラスをリストします。次を実行することにより、実行時にインデックスにアクセスできます。
ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)
Scannotation を試してください。
クラスパスまたはWebアプリケーションのlibディレクトリで特定の注釈を検索するために使用できます。
Springでは、AnnotationUtilsクラスを使用して次のように書くこともできます。すなわち:
Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);
詳細およびすべての異なる方法については、公式ドキュメントを確認してください: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html
ややオフトピックですが、Springも同様のことを行います <context:component-scan> を使用して、おそらくソースコードを学習できますか?
Springは、「ステレオタイプ化された」クラスを自動的に検出する機能を提供します[...]。これらのクラスを自動検出し、対応するBeanを登録するには、[context:component-scan element]を含める必要があります。
答えるには遅すぎますか。 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;
}
}
そして、構成ファイルで、パッケージ名を入れてクラスにアンマーシャルします。
クラスローディングは「オンデマンド」アクティビティであるため、Classloader APIには「列挙」メソッドがありません。通常、クラスパスには数千のクラスがあり、その一部のみが必要です(rt.jar今日では48MBだけです!)。
したがって、couldすべてのクラスを列挙しても、これは非常に時間とメモリを消費します。
単純なアプローチは、セットアップファイル(xmlまたはあなたの好みに合ったもの)に関係するクラスをリストすることです。これを自動的に行う場合は、1つのJARまたは1つのクラスディレクトリに制限してください。
役立つかどうかはわかりませんが、Apache commons-discoveryプロジェクトを調べることができます。
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);
Google Reflection インターフェースも検出したい場合。
Spring ClassPathScanningCandidateComponentProvider
はインターフェイスを検出していません。
Google Reflections はSpringよりもはるかに速いようです。この違いを解決するこの機能リクエストを見つけました: http://www.opensaga.org/jira/browse/OS-738
開発中はアプリケーションの起動時間が非常に重要であるため、これがReflectionsを使用する理由です。リフレクションは、私のユースケースでも非常に使いやすいようです(インターフェイスのすべての実装者を見つけてください)。
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;
}
Scalaライブラリが必要な場合は、Sclasnerを使用します。 https://github.com/ngocdaothanh/sclasner