Dieser Artikel bietet einen kurzen Überblick über drei Tools zum Auffinden kommentierter Klassen in einem Java-Projekt.

Katzen trainieren@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation {} @MyAnnotation public class Bar {} @MyAnnotation public class Foo {}
Frühling
Im Frühjahr dient ClassPathScanningCandidateComponentProvider zu diesem Zweck.
Feature: Crawlt in ClassPath und sucht nach Klassen, die die angegebenen Bedingungen erfüllen
Zusätzliche Funktionenhat viele andere Filter (für Typ, für Methoden usw.)
Beispiel
@Benchmark public void spring() { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class)); List<String> collect = scanner .findCandidateComponents("edu.pqdn.scanner") .stream() .map(BeanDefinition::getBeanClassName) .filter(Objects::nonNull) .collect(Collectors.toList()); assertEquals(collect.size(), 2); assertTrue(collect.contains("edu.pqdn.scanner.test.Bar")); assertTrue(collect.contains("edu.pqdn.scanner.test.Foo")); }
Feature: Crawlt in ClassPath und sucht nach Klassen, die die angegebenen Bedingungen erfüllen
Zusätzliche Funktionen- Holen Sie sich alle Untertypen eines Typs
- Alle Typen / Mitglieder mit Anmerkungen versehen
- Holen Sie sich alle Ressourcen, die einem regulären Ausdruck entsprechen
- Rufen Sie alle Methoden mit einer bestimmten Signatur ab, einschließlich Parameter, Parameteranmerkungen und Rückgabetyp
Abhängigkeit <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency>
Beispiel
@Benchmark public void reflection() { Reflections reflections = new Reflections("edu.pqdn.scanner"); Set<Class<?>> set = reflections.getTypesAnnotatedWith(MyAnnotation.class); List<String> collect = set.stream() .map(Class::getCanonicalName) .collect(Collectors.toList()); assertEquals(collect.size(), 2); assertTrue(collect.contains("edu.pqdn.scanner.test.Bar")); assertTrue(collect.contains("edu.pqdn.scanner.test.Foo")); }
Feature: Geht NICHT in ClassPath, stattdessen werden Klassen zur Kompilierungszeit indiziert
Abhängigkeit <dependency> <groupId>org.atteo.classindex</groupId> <artifactId>classindex</artifactId> <version>3.4</version> </dependency>
Katzen trainieren @IndexMyAnnotated public @interface IndexerAnnotation {} @IndexerMyAnnotation public class Bar {} @IndexerMyAnnotation public class Foo {}
Beispiel
@Benchmark public void indexer() { Iterable<Class<?>> iterable = ClassIndex.getAnnotated(IndexerMyAnnotation.class); List<String> list = StreamSupport.stream(iterable.spliterator(), false) .map(aClass -> aClass.getCanonicalName()) .collect(Collectors.toList()); assertEquals(list.size(), 2); assertTrue(list.contains("edu.pqdn.scanner.classindexer.test.Bar")); assertTrue(list.contains("edu.pqdn.scanner.classindexer.test.Foo")); }
Jmh
Benchmark Mode Cnt Score Error Units ScannerBenchmark.indexer avgt 50 0,100 ? 0,001 ms/op ScannerBenchmark.reflection avgt 50 5,157 ? 0,047 ms/op ScannerBenchmark.spring avgt 50 4,706 ? 0,294 ms/op
Fazit
Wie Sie sehen, ist der Indexer das produktivste Werkzeug. Die für die Suche verwendeten Anmerkungen sollten jedoch das Stereotyp @IndexAnnotated aufweisen.
Die anderen beiden Tools arbeiten viel langsamer, aber für ihre Arbeit ist kein Schamanismus mit dem Code erforderlich. Der Nachteil wird vollständig ausgeglichen, wenn die Suche erst zu Beginn der Anwendung erforderlich ist