Инструменты для поиска аннотированных классов в 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.

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


ссылка на оригинал статьи https://habr.com/post/424511/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *