Este artículo proporciona una breve descripción de tres herramientas para encontrar clases anotadas en un proyecto de Java.

Gatos de entrenamiento@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation {} @MyAnnotation public class Bar {} @MyAnnotation public class Foo {}
Primavera
En primavera, ClassPathScanningCandidateComponentProvider sirve para este propósito.
Característica: se arrastra a ClassPath, buscando clases que satisfagan las condiciones dadas
Características adicionalestiene muchos otros filtros (por tipo, por métodos, etc.)
Ejemplo
@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")); }
Característica: se arrastra a ClassPath, buscando clases que satisfagan las condiciones dadas
Características adicionales- obtener todos los subtipos de algún tipo
- obtener todos los tipos / miembros anotados con alguna anotación
- obtener todos los recursos que coincidan con una expresión regular
- Obtenga todos los métodos con firma específica, incluidos parámetros, anotaciones de parámetros y tipo de retorno
dependencia <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency>
Ejemplo
@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")); }
Característica: NO entra en ClassPath, en cambio, las clases se indexan en tiempo de compilación
dependencia <dependency> <groupId>org.atteo.classindex</groupId> <artifactId>classindex</artifactId> <version>3.4</version> </dependency>
Gatos de entrenamiento @IndexMyAnnotated public @interface IndexerAnnotation {} @IndexerMyAnnotation public class Bar {} @IndexerMyAnnotation public class Foo {}
Ejemplo
@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
Conclusión
Como puede ver, el indexador es la herramienta más productiva, sin embargo, las anotaciones utilizadas para buscar deben tener el estereotipo @IndexAnnotated.
Las otras dos herramientas funcionan mucho más lentamente, pero para su trabajo no se necesita chamanismo con el código. La desventaja se nivela completamente si la búsqueda es necesaria solo al inicio de la aplicación