Un guide de la bibliothèque de réflexions

1. Introduction

La bibliothèque Reflections fonctionne comme un analyseur de chemin de classe. Il indexe les métadonnées scannées et nous permet de les interroger au moment de l'exécution. Il peut également enregistrer ces informations, afin que nous puissions les collecter et les utiliser à tout moment de notre projet, sans avoir à réexaminer le chemin de classe.

Dans ce tutoriel, nous montrerons comment configurer la bibliothèque Reflections et l'utiliser dans nos projets Java.

2. Dépendance de Maven

Pour utiliser Reflections , nous devons inclure sa dépendance dans notre projet:

 org.reflections reflections 0.9.11 

Nous pouvons trouver la dernière version de la bibliothèque sur Maven Central.

3. Configuration des réflexions

Ensuite, nous devons configurer la bibliothèque. Les principaux éléments de la configuration sont les URL et les scanners.

Les URL indiquent à la bibliothèque les parties du chemin de classe à analyser, tandis que les scanners sont les objets qui analysent les URL données.

Dans le cas où aucun scanner n'est configuré, la bibliothèque utilise TypeAnnotationsScanner et SubTypesScanner par défaut.

3.1. Ajouter des URL

Nous pouvons configurer Reflections soit en fournissant les éléments de la configuration en tant que paramètres du constructeur varargs, soit en utilisant l' objet ConfigurationBuilder .

Par exemple, nous pouvons ajouter des URL en instanciant Reflections à l' aide d'une chaîne représentant le nom du package, la classe ou le chargeur de classe:

Reflections reflections = new Reflections("com.baeldung.reflections"); Reflections reflections = new Reflections(MyClass.class); Reflections reflections = new Reflections(MyClass.class.getClassLoader());

De plus, étant donné que Reflections a un constructeur varargs, nous pouvons combiner tous les types de configurations ci-dessus pour l'instancier:

Reflections reflections = new Reflections("com.baeldung.reflections", MyClass.class);

Ici, nous ajoutons des URL en spécifiant le package et la classe à analyser.

Nous pouvons obtenir les mêmes résultats en utilisant le ConfigurationBuilder :

Reflections reflections = new Reflections(new ConfigurationBuilder() .setUrls(ClasspathHelper.forPackage("com.baeldung.reflections"))));

Avec la méthode forPackage () , Classp ath Helpe r fournit d'autres méthodes, telles que forClass () et forClassLoader () , pour ajouter des URL à la configuration.

3.2. Ajout de scanners

La bibliothèque Reflections est fournie avec de nombreux scanners intégrés:

  • FieldAnnotationsScanner - recherche les annotations du champ
  • MethodParameterScanner - scanne les méthodes / constructeurs, puis indexe les paramètres et retourne les annotations de type et de paramètre
  • MethodParameterNamesScanner - inspecte les méthodes / constructeurs, puis indexe les noms de paramètres
  • TypeElementsScanner - examine les champs et les méthodes, puis stocke le nom complet en tant que clé et les éléments en tant que valeurs
  • MemberUsageScanner - analyse les utilisations des méthodes / constructeurs / champs
  • TypeAnnotationsScanner - recherche les annotations d'exécution de la classe
  • SubTypesScanner - recherche les super classes et les interfaces d'une classe, permettant une recherche inversée pour les sous-types
  • MethodAnnotationsScanner - recherche les annotations de méthode
  • ResourcesScanner - collecte toutes les ressources non-classe dans une collection

Nous pouvons ajouter des scanners à la configuration en tant que paramètres du constructeur de Reflections .

Par exemple, ajoutons les deux premiers scanners de la liste ci-dessus:

Reflections reflections = new Reflections("com.baeldung.reflections"), new FieldAnnotationsScanner(), new MethodParameterScanner());

Encore une fois, les deux scanners peuvent être configurés à l' aide de la classe d'assistance ConfigurationBuilder :

Reflections reflections = new Reflections(new ConfigurationBuilder() .setUrls(ClasspathHelper.forPackage("com.baeldung.reflections")) .setScanners(new FieldAnnotationsScanner(), new MethodParameterScanner()));

3.3. Ajout du service ExecutorService

En plus des URL et des scanners, Reflections nous donne la possibilité d'analyser de manière asynchrone le chemin de classe en utilisant ExecutorService .

Nous pouvons l'ajouter en tant que paramètre du constructeur de Reflections , ou via le ConfigurationBuilder :

Reflections reflections = new Reflections(new ConfigurationBuilder() .setUrls(ClasspathHelper.forPackage("com.baeldung.reflections")) .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner()) .setExecutorService(Executors.newFixedThreadPool(4)));

Une autre option consiste simplement à appeler la méthode useParallelExecutor () . Cette méthode configure un FixedThreadPool ExecutorService par défaut avec une taille égale au nombre de processeurs principaux disponibles.

3.4. Ajout de filtres

Un autre élément de configuration important est un filtre. Un filtre indique aux scanners ce qu'il faut inclure et ce qu'il faut exclure lors de l'analyse du chemin de classe .

À titre d'illustration, nous pouvons configurer le filtre pour exclure l'analyse du package de test:

Reflections reflections = new Reflections(new ConfigurationBuilder() .setUrls(ClasspathHelper.forPackage("com.baeldung.reflections")) .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner()) .filterInputsBy(new FilterBuilder().excludePackage("com.baeldung.reflections.test")));

Maintenant, jusqu'à ce point, nous avons fait un bref aperçu des différents éléments de la configuration de Reflections . Ensuite, nous verrons comment utiliser la bibliothèque.

4. Interrogation à l'aide de réflexions

Après avoir appelé l'un des constructeurs Reflections , les scanners configurés analysent toutes les URL fournies. Ensuite, pour chaque scanner, la bibliothèque met les résultats dans les magasins Multimap . Par conséquent, pour utiliser Reflections , nous devons interroger ces magasins en appelant les méthodes de requête fournies.

Voyons quelques exemples de ces méthodes de requête.

4.1. Sous-types

Commençons par récupérer tous les scanners fournis par Reflections :

public Set
    
      getReflectionsSubTypes() { Reflections reflections = new Reflections( "org.reflections", new SubTypesScanner()); return reflections.getSubTypesOf(Scanner.class); }
    

4.2. Types annotés

Ensuite, nous pouvons obtenir toutes les classes et interfaces qui implémentent une annotation donnée.

Alors, récupérons toutes les interfaces fonctionnelles du package java.util.function :

public Set
    
      getJDKFunctinalInterfaces() { Reflections reflections = new Reflections("java.util.function", new TypeAnnotationsScanner()); return reflections.getTypesAnnotatedWith(FunctionalInterface.class); }
    

4.3. Méthodes annotées

Maintenant, utilisons le MethodAnnotationsScanner pour obtenir toutes les méthodes annotées avec une annotation donnée:

public Set getDateDeprecatedMethods() { Reflections reflections = new Reflections( "java.util.Date", new MethodAnnotationsScanner()); return reflections.getMethodsAnnotatedWith(Deprecated.class); }

4.4. Constructeurs annotés

En outre, nous pouvons obtenir tous les constructeurs obsolètes:

public Set getDateDeprecatedConstructors() { Reflections reflections = new Reflections( "java.util.Date", new MethodAnnotationsScanner()); return reflections.getConstructorsAnnotatedWith(Deprecated.class); }

4.5. Paramètres des méthodes

De plus, nous pouvons utiliser MethodParameterScanner pour trouver toutes les méthodes avec un type de paramètre donné:

public Set getMethodsWithDateParam() { Reflections reflections = new Reflections( java.text.SimpleDateFormat.class, new MethodParameterScanner()); return reflections.getMethodsMatchParams(Date.class); }

4.6. Type de retour des méthodes

De plus, nous pouvons également utiliser le même scanner pour obtenir toutes les méthodes avec un type de retour donné.

Let's imagine that we want to find all the methods of the SimpleDateFormat that return void:

public Set getMethodsWithVoidReturn() { Reflections reflections = new Reflections( "java.text.SimpleDateFormat", new MethodParameterScanner()); return reflections.getMethodsReturn(void.class); }

4.7. Resources

Finally, let's use the ResourcesScanner to look for a given filename in our classpath:

public Set getPomXmlPaths() { Reflections reflections = new Reflections(new ResourcesScanner()); return reflections.getResources(Pattern.compile(".*pom\\.xml")); }

4.8. Additional Query Methods

The above were but a handful of examples showing how to use Reflections' query methods. Yet, there are other query methods that we haven't covered here:

  • getMethodsWithAnyParamAnnotated
  • getConstructorsMatchParams
  • getConstructorsWithAnyParamAnnotated
  • getFieldsAnnotatedWith
  • getMethodParamNames
  • getConstructorParamNames
  • getFieldUsage
  • getMethodUsage
  • getConstructorUsage

5. Integrating Reflections into a Build Lifecycle

Nous pouvons facilement intégrer Reflections dans notre build Maven en utilisant le plugin gmavenplus .

Configurons-le pour enregistrer le résultat des analyses dans un fichier:

 org.codehaus.gmavenplus gmavenplus-plugin 1.5   generate-resources  execute          

Plus tard, en appelant la méthode collect () , nous pouvons récupérer les résultats enregistrés et les rendre disponibles pour une utilisation ultérieure, sans avoir à effectuer une nouvelle analyse:

Reflections reflections = isProduction() ? Reflections.collect() : new Reflections("com.baeldung.reflections");

6. Conclusion

Dans cet article, nous avons exploré la bibliothèque Reflections . Nous avons couvert différents éléments de configuration et leurs utilisations. Et, enfin, nous avons vu comment intégrer Reflections dans le cycle de vie de build d'un projet Maven.

Comme toujours, le code complet est disponible à l'adresse over sur GitHub.