大家可以先从第二章「写一个注解」开始看,然后遇到不懂的再回看第一章节中对应的知识。
1. 前置知识
1.1 反射
1.2 注解
2. 写一个注解
在本小节,我们实现一个 InitAnnotation 注解,该注解的功能是在该类加载的时候自动执行。
2.1 新建
新建一个类型为 Annotation
的接口,创建好之后我们可以发现,其实就是一个 @interface
开头的特殊的接口
public @interface InitAnnotation {
}
2.2 增加元注解
- 添加
@Target
元注解:@Target
元注解表明该注解被应用在哪里,可以的选择有以下选择:
ElementType.TYPE
: 应用在类、接口(包括注解类型)或者枚举的声明上ElementType.FIELD
: 应用在成员变量或者枚举常量上ElementType.METHOD
: 应用在方法上ElementType.PARAMETER
: 应用在参数的声明上ElementType.CONSTRUCTOR
: 应用在构造函数的声明上ElementType.LOCAL_VARIABLE
: 应用在局部变量的声明上ElementType.ANNOTATION_TYPE
: 应用在注解的声明上ElementType.PACKAGE
: 应用在包的声明上ElementType.TYPE_PARAMETER
: 应用在类型变量的声明上,Java 1.8新增ElementType.TYPE_USE
: 应用在类型的使用上,Java 1.8新增
- 添加
@Retention
元注解:表明该注解被保留的阶段,有以下几种选择
RetentionPolicy.SOURCE
:注解仅存在于源码中,在class字节码文件中不包含。RetentionPolicy.CLASS
:默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得。RetentionPolicy.RUNTIME
:注解会在class字节码文件中存在,在运行时可以通过反射获取到。
@Target(ElementType.METHOD)
表示注解的作用目标为方法;@Retention(RetentionPolicy.RUNTIME)
保留策略要使用RUNTIME,将注解保留到运行时,这样才能在运行时使用反射来获取到注解。
至此,我们已经完成注解的编写,注解内部不带有处理的逻辑,而是在运行时通过反射机制获取到类内哪些元素被添加了注解,然后再对这些添加了注解的元素进行特殊处理。
2.3 给需要应用注解的地方添加注解
我们以「方法注解」为示意来进行讲解。我们新建一个 InitClazz
类,并且为类中的 init()
方法加上我们之前创建的 @InitAnnotation
注解。
public class InitClazz {
@InitAnnotation
public void init() {
System.out.println("init...");
}
public void run() {
}
private void display() {
}
}
2.4 编写测试类
编写一个测试类,在该测试类中,我们首先通过反射获取到 2.3 类中的所有方法,然后通过 isAnnotationPresent
方法判断类中的方法是否加了 InitAnnotation
注解,如果加了该注解,就通过 invoke 方法运行该方法
public class AnnotationTest {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("MyAnnotation.InitClazz");
Method[] methods = c1.getMethods(); // 获取类中的所有方法
for (Method method : methods) {
boolean isInit = method.isAnnotationPresent(InitAnnotation.class); // 校验某个方法是否具有该注解
// 如果加了 InitAnnotation 注解,就执行该方法
if (isInit) {
method.invoke(c1.newInstance(), null);
}
}
}
}