一、简介
众所周知,在Java 世界中,注解是获取有关类和方法的元信息的一种非常有用的方式。
在本教程中,我们将讨论在运行时扫描Java 注释。
2. 定义自定义注解
让我们首先定义一个示例注释和一个使用我们自定义注释的示例类:
@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
}
}
现在,我们已准备好在基于类和基于方法的用法上解析此自定义注释的“名称”属性。
3. 使用Java 反射扫描注解
在JavaReflection
的帮助下,我们可以扫描特定的带注释的类或特定类的带注释的方法。
为了实现这个目标,我们需要使用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. 使用Spring 上下文库扫描注解
另一种扫描注释的方法是使用Spring Context 库中包含的ClassPathScanningCandidateComponentProvider
类。
让我们从添加[spring-context](https://search.maven.org/search?q=g:org.springframework%20a:spring-context)
依赖开始:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
让我们继续一个简单的例子:
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));
与JavaReflections
完全不同的是,我们可以扫描所有的类而不需要知道具体的类名。
5. 使用Spring Core Library 扫描注解
尽管Spring Core 并没有直接提供对我们代码中所有注解的完整扫描,但我们仍然可以通过使用该库的一些实用程序类来开发自己的完整注解扫描器。
首先,我们需要添加[spring-core](https://search.maven.org/search?q=g:org.springframework%20a:spring-core)
依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.22</version>
</dependency>
这里有一个简单的例子:
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));
在AnnotationUtils
和ClassUtils
的帮助下,可以简单地找到使用特定注解注解的方法和类。
6. 使用反射库扫描注释
Reflections
是一个据说是按照Scannotations
库的精神编写的库。它扫描并索引项目的类路径元数据。
让我们添加[reflections](https://search.maven.org/search?q=g:org.reflections%20a:reflections)
依赖:
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
现在我们准备好使用该库来搜索带注释的类、方法、字段和类型:
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));
正如我们所见,Reflections
库提供了一种灵活的方式来扫描所有带注释的类和方法。所以我们不需要从SampleAnnotatedClass
开始。
7. 使用Jandex 库扫描注解
现在让我们看看另一个名为Jandex
的库,我们可以通过读取我们代码生成的Jandex
文件来在运行时扫描注释。
这个库引入了一个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>
可以看到,运行maven-install
命令后,在META-INF
目录下的classpath中生成了文件名为jandex.idx.
如果需要,我们也可以使用Maven 的rename 插件来修改文件名。
现在我们准备扫描任何类型的注释。首先,我们需要添加[jandex](https://search.maven.org/search?q=g:org.jboss%20a:jandex)
依赖:
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<version>2.4.3.Final</version>
</dependency>
现在是时候扫描注释了:
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. 结论
根据我们的要求;在运行时有多种扫描注解的方法。这些方法中的每一种都有自己的优点和缺点。我们可以决定考虑我们需要什么。
0 评论