(四):自定义注解的运行时解析

在本系列的头三个讲解中,我们学习了什么是注解、如何定义注解、以及如何配置注解。那么当做好了这一切,这样的注解在实际中又有什么样的作用呢?这就是我们今天要讨论的:让编写期配置在Java文件的注解影响运行期的效果,即下图的第三步。

回顾

我们在前面定义了一个叫LovoAnnotation的注解,并且把它配置在了类Student上,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LovoAnnotation{
public String name();
int age() default 18;
int[] scores();
}

1
2
3
4
5
6
7
8
@LovoAnnotation(name="zhang3",age=24,scores={85,67,72})
public class Student{
public void study(int times){
for(int i = 0; i < times; i++){
System.out.println("Good Good Study, Day Day Up!");
}
}
}

那么接下来,我们就以此为例来演示在运行时解析配置在Student类上面的@LovoAnnotation的信息。

运行时解析

确认注解的保持力

在定义注解的时候有一个元注解叫@Retention。这个注解是专门用来描述自定义注解的保持力。说白了就是这个注解能在存在于源代码、class文件、运行期三个阶段中的哪一个。只有当一个注解,使用了@Retention(RetentionPolicy.RUNTIME)修饰,才表示这个注解能够保持到运行期。也只有这种情况我们才可以在运行期获取到它。所以,首先一定要确认这一点。

反射操作获取注解

在运行期探究和使用编译期的内容,当然要用到Java中的开挂技术—反射!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//获取Student的Class对象
Class stuClass = Class.forName("com.lovo.bean.Student");
if(stuClass.isAnnotationPresent(LovoAnnotation.class)){
System.out.println("Student类上配置了LovoAnnotation注解");
LovoAnnotation anno = (LovoAnnotation)
stuClass.getAnnotation(LovoAnnotation.class);
String name = anno.name();
int age = anno.age();
System.out.println(name + " " + age);
}else{
System.out.println("Student类上没有配置LovoAnnotation注解");
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

解释一下:

  1. 如果我们要获得的注解是配置在类型上的,那么我们要从Class 对象上获取;如果是配置在属性、构造、普通方法等元素上,那么要从该属性对应的Field对象、该构造对应的Constructor对象、该方法对应的Method对象等反射对象上获取;
  2. isAnnotationPresent(注解.class)方法是专门判断是否配置有某个指定的注解对象;
  3. getAnnotation(注解.class)方法是获取指定的注解,然后在调用该注解的注解类型元素方法就可以获得配置时的值数据;
  4. 反射对象上还有一个方法getAnnotations(),该方法可以获得该对象身上配置的所有的注解。它会返回给我们一个注解数组,需要注意的是该数组的类型是Annotation类型,这个Annotation是一个来自于java.lang.annotation包的接口(我们曾在本系列的第一章介绍过它,记不住就回头去看看)。
1
Annotation[] allAnnos = stuClass.getAnnotations();
  1. 如果我们的注解使用了元注解@Inherited修饰的话,那么Student的子类无需配置也会自动具备来自于父类的注解。注意:这个特性仅限于@Target为ElementType.TYPE的注解。
1
2
public class LovoStudent extends Student{
}

大家自己测试一下这个结论吧。

总结

至此,我们就可以在运行期获得编写java文件时配置的各种注解了。虽然我们写了好几篇博客才搞定,但是其实语法并不复杂,只是我们讲解的比较细致而已。
在日常开发中,我们大家什么时候感受使用注解最多呢?当然是在各种JavaEE框架的配置中,我们很明显感受到以前很多框架的老版本都是使用的XML文件做配置,而今很多框架的新版本都改成了支持注解配置的方式。
那么,注解真的可以用来完全替代XML吗?请关注本系列的最后一节。
系列:
注解Annotation全解析(一):我不是注释
注解Annotation全解析(二):自定义注解的定义
注解Annotation全解析(三):自定义注解的配置使用
注解Annotation全解析(四):自定义注解的运行时解析
注解Annotation全解析(五):注解与XML的比较

坚持原创技术分享,您的支持将鼓励我继续创作!