天天看点

Java自定义注解(Annotation)、元注解、注解处理器

注解为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

    保留策略,标明该注解的有效范围,有

    SOURCE/CLASS/RUNTIME

    供3种,分别保留到源码、编译后的字节码文件和运行时。详见

    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());
}
           

继续阅读