Как стать автором
Обновить

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

Время на прочтение 3 мин
Количество просмотров 9.5K

В статье приведен небольшой обзор трех инструментов для поиска аннотированных классов в 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.


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

Теги:
Хабы:
+9
Комментарии 3
Комментарии Комментарии 3

Публикации

Истории

Работа

Java разработчик
359 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн