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