1. Introduction
1.绪论
As we know, in the Java world, annotation is a very useful way of obtaining meta information about classes and methods.
正如我们所知,在Java世界中,注解是获得类和方法的元信息的一种非常有用的方式。
In this tutorial, we’re going to discuss scanning Java annotations at runtime.
在本教程中,我们将讨论在运行时扫描Java注释。
2. Defining Custom Annotation
2.定义自定义注释
Let’s start by defining a sample annotation and a sample class that uses our custom annotation:
让我们从定义一个样本注解和一个使用我们自定义注解的样本类开始。
@Target({ METHOD, TYPE })
@Retention(RUNTIME)
public @interface SampleAnnotation {
String name();
}
@SampleAnnotation(name = "annotatedClass")
public class SampleAnnotatedClass {
@SampleAnnotation(name = "annotatedMethod")
public void annotatedMethod() {
//Do something
}
public void notAnnotatedMethod() {
//Do something
}
}
Now, we are ready to resolve the “name” attribute of this custom annotation on both class-based and method-based usages.
现在,我们已经准备好在基于类和基于方法的使用上解决这个自定义注解的 “名称 “属性。
3. Scanning Annotations with Java Reflection
3.用Java反思扫描注解
With the help of Java Reflection, we can scan a specific annotated class or annotated method(s) of a specific class.
在Java Reflection的帮助下,我们可以扫描特定注释的类或特定类的注释方法。
In order to achieve this goal, we need to load the class by using ClassLoader. So this method is useful when we know in which class(es) to scan annotation:
为了实现这一目标,我们需要通过使用ClassLoader来加载类。因此,当我们知道要在哪个类中扫描注解时,这个方法就很有用。
Class<?> clazz = ClassLoader.getSystemClassLoader()
.loadClass("com.baeldung.annotation.scanner.SampleAnnotatedClass");
SampleAnnotation classAnnotation = clazz.getAnnotation(SampleAnnotation.class);
Assert.assertEquals("SampleAnnotatedClass", classAnnotation.name());
Method[] methods = clazz.getMethods();
List<String> annotatedMethods = new ArrayList<>();
for (Method method : methods) {
SampleAnnotation annotation = method.getAnnotation(SampleAnnotation.class);
if (annotation != null) {
annotatedMethods.add(annotation.name());
}
}
Assert.assertEquals(1, annotatedMethods.size());
Assert.assertEquals("annotatedMethod", annotatedMethods.get(0));
4. Scanning Annotations with Spring Context Library
4.用Spring上下文库扫描注解
Another way of scanning the annotations is using ClassPathScanningCandidateComponentProvider class which is included in the Spring Context library.
另一种扫描注解的方法是使用ClassPathScanningCandidateComponentProvider类,它包含在Spring Context库中。
Let’s start by adding spring-context dependency:
让我们从添加spring-context依赖性开始。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
And let’s continue with a simple example:
让我们继续用一个简单的例子。
ClassPathScanningCandidateComponentProvider provider =
new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(SampleAnnotation.class));
Set<BeanDefinition> beanDefs = provider
.findCandidateComponents("com.baeldung.annotation.scanner");
List<String> annotatedBeans = new ArrayList<>();
for (BeanDefinition bd : beanDefs) {
if (bd instanceof AnnotatedBeanDefinition) {
Map<String, Object> annotAttributeMap = ((AnnotatedBeanDefinition) bd)
.getMetadata()
.getAnnotationAttributes(SampleAnnotation.class.getCanonicalName());
annotatedBeans.add(annotAttributeMap.get("name").toString());
}
}
Assert.assertEquals(1, annotatedBeans.size());
Assert.assertEquals("SampleAnnotatedClass", annotatedBeans.get(0));
Completely different from Java Reflections, we can scan all classes without the need to know the specific class names.
与Java Reflections完全不同,我们可以扫描所有的类而不需要知道具体的类名。
5. Scanning Annotations with Spring Core Library
5.用Spring核心库扫描注解
Although Spring Core doesn’t directly provide full scanning of all annotations in our code, we can still develop our own full annotation scanner by using some utility classes of this library.
尽管Spring Core并没有直接提供对我们代码中所有注解的全面扫描,我们仍然可以通过使用这个库的一些实用类来开发我们自己的全面注解扫描器。
First, we need to add spring-core dependency:
首先,我们需要添加spring-core依赖性。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.22</version>
</dependency>
Here comes a simple example:
这里有一个简单的例子。
Class<?> userClass = ClassUtils.getUserClass(SampleAnnotatedClass.class);
List<String> annotatedMethods = Arrays.stream(userClass.getMethods())
.filter(method -> AnnotationUtils
.getAnnotation(method, SampleAnnotation.class) != null)
.map(method -> method.getAnnotation(SampleAnnotation.class)
.name())
.collect(Collectors.toList());
Assert.assertEquals(1, annotatedMethods.size());
Assert.assertEquals("annotatedMethod", annotatedMethods.get(0));
With the help of AnnotationUtils and ClassUtils, it’s possible to find the methods and classes annotated with a specific annotation.
在AnnotationUtils和ClassUtils的帮助下,我们可以找到用特定注解的方法和类。
6. Scanning Annotations with Reflections Library
6.用反射库扫描注释
Reflections is a library that is said to be written in the spirit of Scannotations library. It scans and indexes the project’s classpath metadata.
Reflections是一个据说是按照Scannotations库的精神编写的库。它对项目的classpath元数据进行扫描和索引。
Let’s add reflections dependency:
让我们添加reflections依赖。
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
Now we are ready to use the library for searching the annotated classes, methods, fields and types:
现在我们已经准备好使用这个库来搜索注释的类、方法、字段和类型。
Reflections reflections = new Reflections("com.baeldung.annotation.scanner");
Set<Method> methods = reflections
.getMethodsAnnotatedWith(SampleAnnotation.class);
List<String> annotatedMethods = methods.stream()
.map(method -> method.getAnnotation(SampleAnnotation.class)
.name())
.collect(Collectors.toList());
Assert.assertEquals(1, annotatedMethods.size());
Assert.assertEquals("annotatedMethod", annotatedMethods.get(0));
Set<Class<?>> types = reflections
.getTypesAnnotatedWith(SampleAnnotation.class);
List<String> annotatedClasses = types.stream()
.map(clazz -> clazz.getAnnotation(SampleAnnotation.class)
.name())
.collect(Collectors.toList());
Assert.assertEquals(1, annotatedClasses.size());
Assert.assertEquals("SampleAnnotatedClass", annotatedClasses.get(0));
As we can see, the Reflections library provides a flexible way of scanning all annotated classes and methods. So we don’t need to start with SampleAnnotatedClass.
我们可以看到,Reflections库提供了一种灵活的方式来扫描所有的注解类和方法。所以我们不需要从SampleAnnotatedClass开始。
7. Scanning Annotations with Jandex Library
7.用Jandex库扫描注释
Now let’s take a look at a different library named Jandex which we can use to scan the annotations at runtime by reading the generated Jandex files of our code.
现在让我们来看看一个名为Jandex的不同的库,我们可以通过读取我们代码中生成的Jandex文件,在运行时扫描注释。
This library introduces a Maven plugin to generate a Jandex file that contains meta information related to our project:
该库引入了一个Maven插件,用于生成一个Jandex文件,其中包含与我们项目有关的元信息。
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>1.2.3</version>
<executions>
<execution>
<phase>compile</phase>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<directory>${project.build.outputDirectory}</directory>
</fileSet>
</fileSets>
</configuration>
</execution>
</executions>
</plugin>
As we can see, after running the maven-install command, the file is generated in the classpath under the META-INF directory with the name jandex.idx. We can also modify the name of the file with rename plugin of Maven if it’s needed.
我们可以看到,运行maven-install命令后,文件在META-INF目录下的classpath中生成,名称为jandex.idx.。如果需要,我们还可以用Maven的rename插件修改该文件的名称。
Now we are ready to scan any kind of annotation. First, we need to add the jandex dependency:
现在我们已经准备好扫描任何种类的注释。首先,我们需要添加jandex依赖项。
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<version>2.4.3.Final</version>
</dependency>
Now it’s time to scan the annotations:
现在是扫描注释的时候了。
IndexReader reader = new IndexReader(appFile.getInputStream());
Index jandexFile = reader.read();
List<AnnotationInstance> appAnnotationList = jandexFile
.getAnnotations(DotName
.createSimple("com.baeldung.annotation.scanner.SampleAnnotation"));
List<String> annotatedMethods = new ArrayList<>();
List<String> annotatedClasses = new ArrayList<>();
for (AnnotationInstance annotationInstance : appAnnotationList) {
if (annotationInstance.target().kind() == AnnotationTarget.Kind.METHOD) {
annotatedMethods.add(annotationInstance.value("name")
.value()
.toString());
}
if (annotationInstance.target().kind() == AnnotationTarget.Kind.CLASS) {
annotatedClasses.add(annotationInstance.value("name")
.value()
.toString());
}
}
Assert.assertEquals(1, annotatedMethods.size());
Assert.assertEquals("annotatedMethod", annotatedMethods.get(0));
Assert.assertEquals(1, annotatedClasses.size());
Assert.assertEquals("SampleAnnotatedClass", annotatedClasses.get(0));
8. Conclusion
8.结语
Depending on our requirement; there are various ways of scanning annotations at runtime. Each of these ways has its own pros and cons. We can decide considering what we need.
根据我们的要求,在运行时有各种扫描注释的方式。每种方式都有自己的优点和缺点。我们可以考虑自己的需要来决定。
The implementations of these examples are available over on GitHub.
这些例子的实现可以在GitHub上找到,。