Numérisation des annotations Java à l'exécution
Quelle est la meilleure façon de rechercher toute la classe pour une classe annotée?
je suis en train de faire une bibliothèque et je veux permettre aux utilisateurs d'annoter leurs classes, donc quand l'application Web démarre je dois scanner l'ensemble classpath pour certaines annotations.
connaissez-vous une bibliothèque ou une installation Java pour faire cela?
Modifier: je pense à quelque chose comme la nouvelle fonctionnalité pour les services web Java EE 5 ou EJB. Vous annotez votre classe avec @WebService
ou @EJB
et le système trouve ces classes lors du chargement afin qu'elles soient accessibles à distance.
17 réponses
Utiliser org.springframework.cadre.annotation.ClassPathScanningCandidateComponentprovider
API
un fournisseur de composants qui scanne classpath à partir d'un paquet de base. Elle applique ensuite des filtres d'exclusion et d'inclusion aux classes résultantes pour trouver des candidats.
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());
et une autre solution est Google reflections .
examen Rapide:
- Printemps solution est la voie à suivre si vous êtes à l'aide de Printemps. Sinon, c'est une grande dépendance.
- utiliser ASM directement est un peu encombrant.
- en utilisant Java Assist directement est clunky aussi.
- est très léger et pratique. Pas de maven integration encore.
- Google reflections tire dans les collections Google. Index tout et ensuite est super rapide.
(Edit: FastClasspathScanner has now been renewed to ClassGraph)
Try ClassGraph . (Avertissement, je suis l'auteur). ClassGraph supporte la numérisation d'annotation, soit à l'exécution ou à la compilation, mais aussi beaucoup plus. ClassGraph peut construire une représentation abstraite de l'ensemble du Graphe de classe (toutes les classes, annotations, méthodes, paramètres de méthode, et champs) en mémoire, pour toutes les classes sur le chemin de classe, ou pour les classes sur liste blanche des paquets, et vous pouvez interroger cette classe de graphe comme vous le souhaitez. ClassGraph prend en charge plus de mécanismes de spécification classpath et classloaders que tout autre scanner, et fonctionne également de manière transparente avec le nouveau système de module JPMS, donc si vous basez votre code sur ClassGraph, votre code sera portable au maximum. voir L'API ici.
si vous voulez vraiment une solution light weight (pas de dépendances, API simple, fichier jar 15 kb) et very fast , jetez un oeil à annotation-detector
trouvé à https://github.com/rmuller/infomas-asl
clause de non-responsabilité: je suis l'auteur.
vous pouvez utiliser Java Pluggable Annotation Processing API pour écrire le processeur d'annotation qui sera exécuté pendant le processus de compilation et qui recueillera toutes les classes annotées et construira le fichier d'index pour l'utilisation en exécution.
c'est le moyen le plus rapide possible pour faire la découverte de classe annotée parce que vous n'avez pas besoin de scanner votre chemin de classe à l'exécution, qui est généralement opération très lente. Aussi cette approche fonctionne avec n'importe quel classloader et pas seulement URLClassLoaders est généralement supporté par des scanners d'exécution.
le mécanisme ci-dessus est déjà mis en œuvre dans la bibliothèque ClassIndex .
pour l'utiliser annotez votre annotation personnalisée avec @IndexAnnotated meta-annotation. Cela créera au moment de la compilation un fichier index: META-INF/annotations/com/test/YourCustomAnnotation énumérant toutes les classes annotées. Vous pouvez accéder à l'index à l'exécution en exécutant:
ClassIndex.getAnnotated(com.test.YourCustomAnnotation.class)
utilisez le ServiceLoader , ou implémentez votre propre si vous n'êtes pas en Java 6.
peut-être qu'un processeur d'annotation pourrait produire les fichiers nécessaires sous META-INF/services au moment de la compilation.
Essayer Scannotation .
il peut être utilisé pour rechercher classpath ou votre application web lib directory pour des annotations spécifiques.
vous pourriez vouloir utiliser http://code.google.com/p/annovention/
légèrement offtopique, mais Spring fait aussi quelque chose de similaire, en utilisant < context: component-scan> , dont vous pourriez peut-être étudier le code source de?
Printemps offre la possibilité de détecter automatiquement stéréotypées des " classes [...]. Pour autodétecter ces classes et enregistrer les haricots correspondants, il faut inclure l'élément [context:component-scan].
avec Spring vous pouvez aussi écrire ce qui suit en utilisant la classe AnnotationUtils. c'est à dire:
Class<?> clazz = AnnotationUtils.findAnnotationDeclaringClass(Target.class, null);
pour plus de détails et toutes les méthodes, consultez les documents officiels: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/annotation/AnnotationUtils.html
Je ne suis pas sûr que cela vous aidera ou pas, mais vous pourriez regarder dans le projet Apache communs-découverte.
Est-il trop tard pour répondre. Je dirais, il est préférable d'aller par des bibliothèques comme ClassPathScanningCandidateComponentprovider ou comme Scannotations
mais même si quelqu'un veut essayer quelques mains avec classLoader, j'en ai écrit quelques-unes moi-même pour imprimer les annotations des classes dans un paquet:
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;
}
}
et dans le fichier de configuration, vous mettez le nom du paquet et le démarshall il à un classe.
Java n'a pas de"Discovery". La seule façon que je connaisse est de scanner le répertoire que le .les fichiers de classe devrait être dans les, analyser les noms et les utiliser. Horriblement moche, peut-être qu'il y a un meilleur paquet ces jours-ci--je n'ai pas regardé depuis quelques années.
habituellement, ce problème était résolu en incluant un fichier de propriétés ou un .fichier xml avec les noms de classe.
je serais intéressé par une meilleure réponse.
L'API Classloader n'a pas de méthode" enumerate", car le chargement de classe est une activité" à la demande " -- vous avez généralement des milliers de classes dans votre classepath, dont seulement une fraction sera jamais nécessaire (la rt.jar seul, C'est 48MB de nos jours!).
donc, même si vous pouvait énumérer toutes les classes, cela prendrait beaucoup de temps et de mémoire.
l'approche simple est d'énumérer les classes concernées dans une configuration si vous voulez le faire automatiquement, limitez-vous à un JAR ou à un répertoire de classe.
Google Réflexions semble être beaucoup plus rapide que le Printemps. Trouvé cette requête de caractéristique qui Adresse cette différence: http://www.opensaga.org/jira/browse/OS-738
c'est une raison d'utiliser Reflections car le temps de démarrage de mon application est vraiment important pendant le développement. Réflexions semble également être très facile à utiliser pour mon cas d'utilisation (trouver tous les responsables de l'implémentation d'une interface).
si vous cherchez une alternative à reflections j'aimerais recommander Panda Utilities - AnnotationsScanner . C'est un scanner sans Goyave (Goyave a ~3MB, Panda Utilities a ~200kb) basé sur le code source de la bibliothèque reflections.
il est également dédié aux recherches futures. Si vous souhaitez numériser plusieurs fois des sources incluses ou même fournir une API, ce qui permet à quelqu'un de numériser classpath actuel, AnnotationsScannerProcess
met en cache tous récupérés ClassFiles
, donc c'est vraiment rapide.
exemple Simple de AnnotationsScanner
usage:
AnnotationsScanner scanner = AnnotationsScanner.createScanner()
.includeSources(ExampleApplication.class)
.build();
AnnotationsScannerProcess process = scanner.createWorker()
.addDefaultProjectFilters("net.dzikoysk")
.fetch();
Set<Class<?>> classes = process.createSelector()
.selectTypesAnnotatedWith(AnnotationTest.class);
si vous voulez une bibliothèque Scala, utilisez Sclasner: https://github.com/ngocdaothanh/sclasner