注解的类型

第一类是由编译器使用的注解,例如:

  • @Override:让编译器检查该方法是否正确地实现了覆写;
  • @SuppressWarnings:告诉编译器忽略此处代码产生的警告。

这类注解不会被编译进入.class文件,它们在编译后就被编译器扔掉了。
第二类是由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。这类注解会被编译进入.class文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理。
第三类是在程序运行期能够读取的注解,它们在加载后一直存在于JVM中,这也是最常用的注解。例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)。

注解的定义

  1. public @interface Report {
  2. int type() default 0;
  3. String level() default "info";
  4. String value() default "";
  5. }

使用@interface语法定义注解,通常规定使用default设定一个默认值。并且最常用的参数应当命名为value

元注解

一些可以修饰其他注解的注解成为元注解。Java库已经定义了一些元注解,以供开发者使用。

@Target

使用@Target可以定义Annotation能够被应用于源码的哪些位置:

  • 类或接口:ElementType.TYPE
  • 字段:ElementType.FIELD
  • 方法:ElementType.METHOD
  • 构造方法:ElementType.CONSTRUCTOR
  • 方法参数:ElementType.PARAMETER

    @Retention

    另一个重要的元注解@Retention定义了Annotation的生命周期,若@Retention不存在,则该Annotation默认为CLASS:

  • 仅编译期:RetentionPolicy.SOURCE

  • 仅class文件:RetentionPolicy.CLASS
  • 运行期:RetentionPolicy.RUNTIME

    注解的处理

    一般情况下,注解的读取指的是读取RUNTIME类型的注解,其他两种类型在运行期之前早已经被使用完,而RUNTIME类型的注解会被加载进JVM,并且在运行期可以被程序读取。
    Java提供的使用反射API读取Annotation的方法包括:
    判断某个注解是否存在于ClassFieldMethodConstructor

  • Class.isAnnotationPresent(Class)

  • Field.isAnnotationPresent(Class)
  • Method.isAnnotationPresent(Class)
  • Constructor.isAnnotationPresent(Class)

例如:

// 判断@Report是否存在于Person类:
Person.class.isAnnotationPresent(Report.class);

读取注解的方法:

  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Method.getAnnotation(Class)
  • Constructor.getAnnotation(Class)

使用反射API读取Annotation有两种方法。方法一是先判断Annotation是否存在,如果存在,就直接读取:

Class cls = Person.class;
if (cls.isAnnotationPresent(Report.class)) {
    Report report = cls.getAnnotation(Report.class);
    ...
}

第二种方法是直接读取Annotation,如果Annotation不存在,将返回null

Class cls = Person.class;
Report report = cls.getAnnotation(Report.class);
if (report != null) {
   ...
}

读取方法参数的Annotation较为麻烦,一个方法可能有多个参数,而一个参数可能有多个注解。所以要用一个二维数组来表示。Annotation[][] annos = Method.getParameterAnnotations();。其中annos[0]表示第一个参数的所有注解,同理annos[1][2]表示第二个参数的第三个注解。