Ein Leitfaden zur Reflections Library

1. Einleitung

Die Reflections- Bibliothek fungiert als Klassenpfad-Scanner. Es indiziert die gescannten Metadaten und ermöglicht es uns, sie zur Laufzeit abzufragen. Diese Informationen können auch gespeichert werden, sodass wir sie zu jedem Zeitpunkt während unseres Projekts sammeln und verwenden können, ohne den Klassenpfad erneut scannen zu müssen.

In diesem Tutorial zeigen wir, wie Sie die Reflections- Bibliothek konfigurieren und in unseren Java-Projekten verwenden.

2. Maven-Abhängigkeit

Um Reflections verwenden zu können , müssen wir die Abhängigkeit in unser Projekt aufnehmen:

 org.reflections reflections 0.9.11 

Wir finden die neueste Version der Bibliothek auf Maven Central.

3. Reflektionen konfigurieren

Als nächstes müssen wir die Bibliothek konfigurieren. Die Hauptelemente der Konfiguration sind die URLs und Scanner.

Die URLs teilen der Bibliothek mit, welche Teile des Klassenpfads gescannt werden sollen, während die Scanner die Objekte sind, die die angegebenen URLs scannen.

Für den Fall, dass kein Scanner konfiguriert ist, verwendet die Bibliothek standardmäßig TypeAnnotationsScanner und SubTypesScanner .

3.1. URLs hinzufügen

Wir können Reflections entweder konfigurieren, indem wir die Konfigurationselemente als Parameter des varargs-Konstruktors bereitstellen oder das ConfigurationBuilder- Objekt verwenden.

Zum Beispiel können wir URLs hinzufügen, indem wir Reflections mithilfe einer Zeichenfolge instanziieren , die den Paketnamen, die Klasse oder den Klassenlader darstellt:

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

Da Reflections über einen varargs-Konstruktor verfügt, können wir außerdem alle oben genannten Konfigurationstypen kombinieren, um ihn zu instanziieren:

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

Hier fügen wir URLs hinzu, indem wir das zu scannende Paket und die zu scannende Klasse angeben.

Mit dem ConfigurationBuilder können wir dieselben Ergebnisse erzielen :

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

Zusammen mit der forPackage () -Methode bietet Classp ath Helpe r andere Methoden wie forClass () und forClassLoader () , um der Konfiguration URLs hinzuzufügen.

3.2. Scanner hinzufügen

Die Reflections-Bibliothek enthält viele integrierte Scanner:

  • FieldAnnotationsScanner - Sucht nach Feldanmerkungen
  • MethodParameterScanner - scannt Methoden / Konstruktoren, indiziert dann Parameter und gibt Typ- und Parameteranmerkungen zurück
  • MethodParameterNamesScanner - Überprüft Methoden / Konstruktoren und indiziert dann Parameternamen
  • TypeElementsScanner - Untersucht Felder und Methoden, speichert dann den vollständig qualifizierten Namen als Schlüssel und Elemente als Werte
  • MemberUsageScanner - Scannt die Verwendung von Methoden / Konstruktoren / Feldern
  • TypeAnnotationsScanner - Sucht nach Laufzeitanmerkungen der Klasse
  • SubTypesScanner - sucht Super Klassen und Schnittstellen einer Klasse, ein Reverse - Lookup für Subtypen erlaubt
  • MethodAnnotationsScanner - sucht nach Anmerkungen der Methode
  • ResourcesScanner - Sammelt alle Ressourcen außerhalb einer Klasse in einer Sammlung

Wir können der Konfiguration Scanner als Parameter des Reflections -Konstruktors hinzufügen .

Fügen wir zum Beispiel die ersten beiden Scanner aus der obigen Liste hinzu:

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

Auch hier können die beiden Scanner mithilfe der ConfigurationBuilder- Hilfsklasse konfiguriert werden :

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

3.3. Das Hinzufügen der ExecutorService

Zusätzlich zu URLs und Scannern bietet Reflections die Möglichkeit, den Klassenpfad mithilfe des ExecutorService asynchron zu scannen .

Wir können es als Parameter des Reflections -Konstruktors oder über den ConfigurationBuilder hinzufügen :

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

Eine andere Möglichkeit besteht darin, einfach die useParallelExecutor () -Methode aufzurufen . Diese Methode konfiguriert einen Standard- FixedThreadPool ExecutorService mit einer Größe, die der Anzahl der verfügbaren Kernprozessoren entspricht.

3.4. Filter hinzufügen

Ein weiteres wichtiges Konfigurationselement ist ein Filter. Ein Filter teilt den Scannern mit, was beim Scannen des Klassenpfads eingeschlossen und was ausgeschlossen werden soll .

Zur Veranschaulichung können wir den Filter so konfigurieren, dass das Scannen des Testpakets ausgeschlossen wird:

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")));

Bis zu diesem Punkt haben wir einen schnellen Überblick über die verschiedenen Elemente der Reflections -Konfiguration gegeben. Als nächstes werden wir sehen, wie die Bibliothek verwendet wird.

4. Abfragen mit Reflexionen

Nach dem Aufrufen eines der Reflections- Konstruktoren scannen die konfigurierten Scanner alle bereitgestellten URLs. Anschließend legt die Bibliothek für jeden Scanner die Ergebnisse in Multimap- Speichern ab . Um Reflections verwenden zu können , müssen wir diese Speicher abfragen, indem wir die bereitgestellten Abfragemethoden aufrufen.

Sehen wir uns einige Beispiele für diese Abfragemethoden an.

4.1. Untertypen

Beginnen wir mit dem Abrufen aller von Reflections bereitgestellten Scanner :

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

4.2. Kommentierte Typen

Als nächstes können wir alle Klassen und Schnittstellen abrufen, die eine bestimmte Annotation implementieren.

Lassen Sie uns also alle Funktionsschnittstellen des Pakets java.util.function abrufen :

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

4.3. Kommentierte Methoden

Verwenden Sie jetzt den MethodAnnotationsScanner , um alle Methoden mit einer bestimmten Anmerkung zu versehen:

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

4.4. Kommentierte Konstruktoren

Außerdem können wir alle veralteten Konstruktoren erhalten:

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

4.5. Methodenparameter

Zusätzlich können wir MethodParameterScanner verwenden , um alle Methoden mit einem bestimmten Parametertyp zu finden:

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

4.6. Rückgabetyp der Methoden

Darüber hinaus können wir denselben Scanner auch verwenden, um alle Methoden mit einem bestimmten Rückgabetyp abzurufen.

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

Mit dem gmavenplus-Plugin können wir Reflections einfach in unseren Maven-Build integrieren .

Konfigurieren wir es so, dass das Ergebnis von Scans in einer Datei gespeichert wird:

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

Später können wir durch Aufrufen der Methode collect () die gespeicherten Ergebnisse abrufen und für die weitere Verwendung verfügbar machen, ohne einen neuen Scan durchführen zu müssen:

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

6. Fazit

In diesem Artikel haben wir die Reflections- Bibliothek untersucht. Wir haben verschiedene Konfigurationselemente und deren Verwendung behandelt. Und schließlich haben wir gesehen, wie Reflections in den Build-Lebenszyklus eines Maven-Projekts integriert werden können .

Wie immer ist der vollständige Code auf GitHub verfügbar.