Инструменты для поиска аннотированных классов в Java
В статье приведен небольшой обзор трех инструментов для поиска аннотированных классов в java проекте.
@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation <> @MyAnnotation public class Bar <> @MyAnnotation public class Foo <>
Spring
В Spring для этих целей служит ClassPathScanningCandidateComponentProvider.
имеет множество других фильтров (для типа, для методов и т.д.)
Пример
@Benchmark public void spring() < ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class)); Listcollect = 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
- 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
org.reflections reflections 0.9.11
Пример
@Benchmark public void reflection() < Reflections reflections = new Reflections("edu.pqdn.scanner"); Set> set = reflections.getTypesAnnotatedWith(MyAnnotation.class); List 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
org.atteo.classindex classindex 3.4
@IndexMyAnnotated public @interface IndexerAnnotation <> @IndexerMyAnnotation public class Bar <> @IndexerMyAnnotation public class Foo <>
Пример
@Benchmark public void indexer() < Iterable> iterable = ClassIndex.getAnnotated(IndexerMyAnnotation.class); List 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.
Другие два инструмента работают заметно медленнее, однако для их работы никакого шаманства с кодом производить не надо. Недостаток полностью нивелируется, если поиск необходим только на старте приложения
How do I get all annotations?
In the following example we tried to read all annotations from the sayHi() method. First we need to obtain the method object itself. Because the sayHi() method has parameters, we need to pass not only the method name to the getMethod() method, but we also need to pass the parameter’s type.
The getAnnotations() method returns only annotation that has a RetentionPolicy.RUNTIME , because other retention policy doesn’t allow the annotation to available at runtime.
package org.kodejava.lang.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class GetAllAnnotation < private final Mapdata = new HashMap<>(); public static void main(String[] args) < GetAllAnnotation demo = new GetAllAnnotation(); demo.sayHi("001", "Alice"); demo.sayHi("004", "Malory"); try < Class extends GetAllAnnotation>clazz = demo.getClass(); // To get the sayHi() method we need to pass not only the method // name but also its parameters type so the getMethod() method // return the correct method for us to use. Method method = clazz.getMethod("sayHi", String.class, String.class); // Get all annotations from the sayHi() method. But this actually // will only return one annotation. Because only the HelloAnnotation // annotation that has RUNTIME retention policy, which means that // the other annotations associated with sayHi() method is not // available at runtime. Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) < System.out.println("Type: " + annotation.annotationType()); >> catch (NoSuchMethodException e) < e.printStackTrace(); >> @MyAnnotation("Hi") @HelloAnnotation(value = "Hello", greetTo = "Everyone") public void sayHi(String dataId, String name) < Mapdata = getData(); if (data.containsKey(dataId)) < System.out.println("Hello " + data.get(dataId)); >else < data.put(dataId, name); >> private Map getData() < data.put("001", "Alice"); data.put("002", "Bob"); data.put("003", "Carol"); return data; >>
package org.kodejava.lang.annotation; public @interface MyAnnotation
Check the HelloAnnotation on the following link How do I create a simple annotation?.
The result of this code snippet:
Hello Alice Type: interface org.kodejava.lang.annotation.HelloAnnotation
A programmer, recreational runner and diver, live in the island of Bali, Indonesia. Programming in Java, Spring, Hibernate / JPA. You can support me working on this project, buy me a cup of coffee ☕ every little bit helps, thank you 🙏