Tools for searching annotated classes in Java

  • Tutorial

The article provides a small overview of the three tools for searching annotated classes in a java project.


image


Training cats
@Retention(RetentionPolicy.RUNTIME)
public@interface MyAnnotation {}
@MyAnnotationpublicclassBar{}
@MyAnnotationpublicclassFoo{}

Spring


In Spring, ClassPathScanningCandidateComponentProvider serves for this purpose.


Feature: climbs into ClassPath, looking for classes that meet specified conditions

Additional features

has many other filters (for type, for methods, etc.)


Example


@Benchmarkpublicvoidspring(){
   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"));
}

Reflections


Feature: climbs into ClassPath, looking for classes that meet specified conditions

Additional features
  • get all subtypes of some type
  • get all types / members annotated with some annotation
  • get all resources matching a regular expression
  • parameters, parameter returns and return type

dependency
<dependency><groupId>org.reflections</groupId><artifactId>reflections</artifactId><version>0.9.11</version></dependency>

Example


@Benchmarkpublicvoidreflection(){
   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"));
}

classindex


Feature: Does not climb in ClassPath, instead of this classes are indexed at the compilation stage

dependency
<dependency><groupId>org.atteo.classindex</groupId><artifactId>classindex</artifactId><version>3.4</version></dependency>

Training cats
@IndexMyAnnotatedpublic@interface IndexerAnnotation {}
@IndexerMyAnnotationpublicclassBar{}
@IndexerMyAnnotationpublicclassFoo{}

Example


@Benchmarkpublicvoidindexer(){
   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   ErrorUnits
ScannerBenchmark.indexer     avgt   500,100 ? 0,001  ms/op
ScannerBenchmark.reflection  avgt   505,157 ? 0,047  ms/op
ScannerBenchmark.spring      avgt   504,706 ? 0,294  ms/op

Conclusion


As you can see, the indexer is the most productive tool, however, the annotations for which the search is performed must have the stereotype @IndexAnnotated.


The other two tools are working much slower, but for their work no shamanism with code is needed. The disadvantage is completely leveled out if the search is needed only at the start of the application.


Also popular now: