本文摘录于 https://juejin.cn/post/6844903636733001741#heading-3

一、注解的本质

The common interface extended by all annotation types 所有的注解类型都继承自这个普通的接口(Annotation)

1、本质演示

这句话有点抽象,但却说出了注解的本质,下面看代码证明下这句话
1、首先定义一个注解

  1. public @interface MyAnno2 {
  2. }

2、对其进行编译

  1. superking@wangchaodeMacBook-Pro demo01 % javac MyAnno2.java

产生文件 MyAnno2.class

3、对刚才编译的结果(MyAnno2.class )进行反编译

  1. javap MyAnno2.class

得到结果

  1. public interface demo01.MyAnno2 extends java.lang.annotation.Annotation {
  2. }

注解的本质就是一个继承了 Annotation 接口的接口。

2、使用

一个注解准确意义上来说,它不是程序的一部分,只不过是一种特殊的注释而已(或者可以理解为一种标识),如果没有解析它的代码,它可能连注释都不如。
解析一个类或者方法的注解往往有两种形式,一种是编译期直接的扫描,一种是运行期反射。反射的事情我们待会说,而编译器的扫描指的是编译器在对 java 代码编译字节码的过程中会检测到某个类或者方法被一些注解修饰,这时它就会对于这些注解进行某些处理。
典型的就是注解 @Override,一旦编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的方法签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。
这一种情况只适用于那些编译器已经熟知的注解类,比如 JDK 内置的几个注解,而你自定义的注解,编译器是不知道你这个注解的作用的,当然也不知道该如何处理,往往只是会根据该注解的作用范围来选择是否编译进字节码文件,仅此而已。

二、元注解

1、基本介绍

『元注解』是用于修饰注解的注解,通常用在注解的定义上,例如:

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface Override {
  4. }

这是我们 @Override 注解的定义,你可以看到其中的 @Target,@Retention 两个注解就是我们所谓的『元注解』,『元注解』一般用于指定某个注解生命周期以及作用目标等信息。

JAVA 中有以下几个『元注解』:

  • @Target:注解的作用目标
  • @Retention:注解的生命周期
  • @Documented:注解是否应当被包含在 JavaDoc 文档中
  • @Inherited:是否允许子类继承该注解

    2、元注解详解

    @Target

    image.png
    我们可以通过以下的方式来为这个 value 传值:

    1. @Target(value = {ElementType.FIELD})

    被这个 @Target 注解修饰的注解将只能作用在成员字段上,不能用于修饰方法或者类。其中,ElementType 是一个枚举类型,有以下一些值:

  • ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上

  • ElementType.FIELD:允许作用在属性字段上
  • ElementType.METHOD:允许作用在方法上
  • ElementType.PARAMETER:允许作用在方法参数上
  • ElementType.CONSTRUCTOR:允许作用在构造器上
  • ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
  • ElementType.ANNOTATION_TYPE:允许作用在注解上
  • ElementType.PACKAGE:允许作用在包上

    @Retention

    用于指明当前注解的生命周期,它的基本定义如下:
    image.png
    同样的,它也有一个 value 属性:

    1. @Retention(value = RetentionPolicy.RUNTIME

    这里的 RetentionPolicy 依然是一个枚举类型,它有以下几个枚举值可取:

  • RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件

  • RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
  • RetentionPolicy.RUNTIME:永久保存,可以反射获取

@Retention 注解指定了被修饰的注解的生命周期,一种是只能在编译期可见,编译后会被丢弃,一种会被编译器编译进 class 文件中,无论是类或是方法,乃至字段,他们都是有属性表的,而 JAVA 虚拟机也定义了几种注解属性表用于存储注解信息,但是这种可见性不能带到方法区,类加载时会予以丢弃,最后一种则是永久存在的可见性。
剩下两种类型的注解我们日常用的不多,也比较简单,这里不再详细的进行介绍了,你只需要知道他们各自的作用即可。@Documented 注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。@Inherited 注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。