注解为Java代码中各种程序元素(包、类、方法、成员变量、方法参数、本地变量等)提供了一种附加额外元数据(metadata)信息的能力。
如同
Object
类是所有类的默认父类一样,
java.lang.annotation.Annotation
接口是所有注解的默认父接口。
自定义注解
先来定义一个注解:
@Documented
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationPet {
String name() default "旺财";
}
元注解
Java SE提供了4个用于注解到其他自定义注解上的元注解:
-
用于生成@Documented
文档。JavaDoc
-
使得父类上的注解信息能够通过继承关系传递到子类上。这种情况下,从父类和子类上得到的其实是同一个注解对象。@Inherited
-
接收一个@Target
数组,指明被修饰的注解可以标注在哪些地方,包类、方法、成员变量、方法参数等,详见ElementType[]
枚举类。ElementType
-
保留策略,标明该注解的有效范围,有@Retention
供3种,分别保留到源码、编译后的字节码文件和运行时。详见SOURCE/CLASS/RUNTIME
枚举类。RetentionPolicy
代码验证:
// Pet.java
@AnnotationPet
public class Pet {}
// Dog.java
public class Dog extends Pet {}
public static void main(String[] args) {
// 因为@AnnotationPet注解上有@Inherited标记
// 子类Dog并没有直接被@AnnotationPet注解,但是依然能获取到注解对象
AnnotationPet annotation1 = Dog.class.getAnnotation(AnnotationPet.class);
AnnotationPet annotation2 = Pet.class.getAnnotation(AnnotationPet.class);
// 父类和子类上面的注解对象,其实是同一个对象
System.out.println(annotation1 == annotation2);//true
System.out.println(annotation1);//@AnnotationPet(name=旺财)
// getDeclaredAnnotation忽略继承过来的注解,因此结果是null
System.out.println(Dog.class.getDeclaredAnnotation(AnnotationPet.class));//null
//Annotation接口是所有注解的默认父接口
System.out.println(annotation1 instanceof Annotation);//true
System.out.println(annotation1 instanceof Object);//true
}
注解中可以携带的元数据(metadata)类型:
注解中可以携带的元数据,是有特定类型限制的。并不是自己随便定义一个类都可以作为注解中元数据的类型。注解中支持的元数据类型如下:
- 所有基本类型:boolean/byte/int/long/float等
- String类型
- Class类型
- 自定义的enum枚举类型
- Annotation类型
- 以上类型的数组类型
注解处理器
如何通过Java代码来识别和处理注解中的信息呢?
Java反射包下面,有一个
AnnotatedElement
接口,其中定义了一些提取注解的方法:
// 提取特定类型annotationClass的注解对象,如果没有返回null
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
// 返回直接存在的(忽略继承过来的)特定类型的注解对象
<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
// 特定类型的注解是否存在
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
// 返回存在的所有注解
Annotation[] getAnnotations();
// 返回直接存在的(忽略继承过来的)所有注解
Annotation[] getDeclaredAnnotations();
AnnotatedElement
接口的已知实现此接口的类包括:
Class、Constructor、Field、Method、Package
。
也就是说,通过反射拿到
Class/Constructor/Field/Method/Package
对象后,就可以直接调用相关方法来获取到存在的注解对象。
final Class<?> clazz = Dog.class;
if(clazz.isAnnotationPresent(AnnotationPet.class)) {
final AnnotationPet annotation = clazz.getAnnotation(AnnotationPet.class);
System.out.println("注解中的元数据:name=" + annotation.name());
}