Инструменты для поиска аннотированных классов в Java

в 14:57, , рубрики: java

В статье приведен небольшой обзор трех инструментов для поиска аннотированных классов в java проекте.

image

Тренировочные кошки

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}

@MyAnnotation
public class Bar {}

@MyAnnotation
public class Foo {}

Spring

В Spring для этих целей служит ClassPathScanningCandidateComponentProvider.

Особенность: лезет в ClassPath, ищет классы которые удовлетворяют заданным условиям

Дополнительные возможности

имеет множество других фильтров (для типа, для методов и т.д.)

Пример

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

Reflections

Особенность: лезет в ClassPath, ищет классы которые удовлетворяют заданным условиям

Дополнительные возможности

  • get all subtypes of some type
  • get all types/members annotated with some annotation
  • get all resources matching a regular expression
  • get all methods with specific signature including parameters, parameter annotations and return type

dependency

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

Пример

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

classindex

Особенность: НЕ лезет в ClassPath, вместо это классы индексируются на этапе компиляции

dependency

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

Тренировочные кошки

@IndexMyAnnotated
public @interface IndexerAnnotation {}

@IndexerMyAnnotation
public class Bar {}

@IndexerMyAnnotation
public class Foo {}

Пример

@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

Заключение

Как видно indexer самый производительный инструмент, однако, аннотации по которым производится поиск должны иметь стереотип @IndexAnnotated.

Другие два инструмента работают заметно медленнее, однако для их работы никакого шаманства с кодом производить не надо. Недостаток полностью нивелируется, если поиск необходим только на старте приложения

Автор: PqDn

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js