参考文献

注解概念

  • Java注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制.重点:和 Javadoc 不同,Java 标注可以通过反射获取标注内容.
  • 在编译器生成类文件时,标注 可以被嵌入到字节码中.Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容
  • 注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解.它主要的作用有以下四方面:
    • 生成文档,通过代码里标识的元数据生成javadoc文档.
    • 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证.
    • 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码.
    • 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例
  • 注解是对代码的一种增强,可以在代码编译或者程序运行期间获取注解的信息,然后根据这些信息做各种事情。

Java自带的标准注解

@Override重写

  • 概念:检查该方法是否是重写方法.如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误.

@Deprecated 过期警告

  • 概念:标记过时方法.如果使用该方法,会报编译警告.在开发中,我们经常能遇到这样的情况

@SuppressWarnings 忽略警告

  • 概念:指示编译器去忽略注解中声明的警告.

元注解

@Retention 指定注解的保留策略

  • 概念:表示在什么级别保存该注解信息.在实际开发中,一般都写RUNTIME,除非项目有特殊需求

    注解是代码的特殊标记,可以在编译(RetentionPolicy.SOURCE),类加载(RetentionPolicy.CLASS),运行时被读取(RetentionPolicy.RUNTIME)

  • SOURCECLASS分别需要基础AbstractProcessor,实现process方法去处理自定义注解

  • RUNTIME是日常开发中用的最多的,配合Java反射机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler. 注解只保留在源码中,编译为字节码之后就丢失了,也就是class文件中就不存在了
*/
SOURCE,

/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior. 注解只保留在源码和字节码(class文件)中,运行阶段会丢失
*/
CLASS,

/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
* 源码、字节码、运行期间都存在
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}

@Documented 作用文档

  • 概念:将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档.
  • 无参的注解,作用域为RetentionPolicy.RUNTIME,运行时有用!这个只是用来作为标记,了解即可,在实际运行后会将该注解写入javadoc中,方便查看.

@Target 目标

  • 概念:标记这个注解应该是使用在哪种 Java 成员上面

  • 注意这里是数组格式的参数,证明可以传多个值.

    • @Target(ElementType.TYPE)——接口、类、枚举、注解
    • @Target(ElementType.FIELD)——字段、枚举的常量
    • @Target(ElementType.METHOD)——方法
    • @Target(ElementType.PARAMETER)——方法参数
    • @Target(ElementType.CONSTRUCTOR) ——构造函数
    • @Target(ElementType.LOCAL_VARIABLE)——局部变量
    • @Target(ElementType.ANNOTATION_TYPE)——注解
    • @Target(ElementType.PACKAGE)——包
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
    * Type parameter declaration
    *
    * @since 1.8
    */
    TYPE_PARAMETER,

    /**
    * Use of a type
    *
    * @since 1.8
    */
    TYPE_USE,

    /**
    * Module declaration.
    *
    * @since 9
    */
    MODULE
    }

@Inherited 继承

  • 概念:标记这个注解是继承于哪个注解类(默认注解并没有继承于任何子类).
  • 让子类可以继承父类中被@Inherited修饰的注解,注意是继承父类中的,如果接口中的注解也使用@Inherited修饰了,那么接口的实现类是无法继承这个注解的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.lang.annotation.*;

public class InheritAnnotationTest {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface A1 { //@1
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface A2 { //@2
}

@A1 //@3
interface I1 {
}

@A2 //@4
static class C1 {
}

static class C2 extends C1 implements I1 {
} //@5

public static void main(String[] args) {
for (Annotation annotation : C2.class.getAnnotations()) { //@6
System.out.println(annotation);
}
}
}
// output @org.example.InheritAnnotationTest$A2()

新注解

从 Java 7 开始,额外添加了 3 个注解:

  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告.
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口.
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次.

自定义注解

定义注解

  • jdk中注解相关的类和接口都定义在java.lang.annotation包中。

  • 注解的定义和我们常见的类、接口类似,只是注解使用@interface来定义,如下定义一个名称为MyAnnotation的注解:

    1
    2
    3
    public @interface MyAnnotation { 

    }

注解中定义参数

  • 注解有没有参数都可以,定义参数如下:

    1
    2
    3
    4
    5
    public @interface 注解名称{
      [public] 参数类型 参数名称1() [default 参数默认值];
      [public] 参数类型 参数名称2() [default 参数默认值];
      [public] 参数类型 参数名称n() [default 参数默认值];
    }
    • 注解中可以定义多个参数,参数的定义有以下特点:
      1. 访问修饰符必须为public,不写默认为public
      2. 该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组
      3. 该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来 便利操作)
      4. 参数名称后面的()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法
      5. default代表默认值,值必须和第2点定义的类型一致
      6. 如果没有默认值,代表后续使用注解时必须给该类型元素赋值

注解信息的获取

  • 为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect反射包下新增了AnnotatedElement接口,它主要用于表示目前正在虚拟机中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息.

    说明
    Package 用来表示包的信息
    Class 用来表示类的信息
    Constructor 用来表示构造方法信息
    Field 用来表示类中属性信息
    Method 用来表示方法信息
    Parameter 用来表示方法参数信息
    TypeVariable 用来表示类型变量信息,如: 类上定义的泛型类型变量,方法上面定义的泛型类型变量
AnnotatedElement常用方法
返回值 方法名称 说明
<A extends Annotation> getAnnotation(Class<A> annotationClass) 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null
Annotation[] getAnnotations() 返回此元素上存在的所有注解,包括从父类继承的
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注解,注意,不包括父类的注解,调用者可以随意修改返回 的数组;这不会对其他调用者返回的数组产生任何影响,没有则返回长度为0的数组
解析类上的注解
1
2
3
4
5
6
@Test
public void m1() {
for (Annotation annotation : LoginRequiredInterceptor.class.getAnnotations()) {
System.out.println(annotation);
}
}
解析类上的类型变量
1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void m2() {
TypeVariable<Class<LoginRequiredInterceptor>>[] typeParameters =
LoginRequiredInterceptor.class.getTypeParameters();
for (TypeVariable<Class<LoginRequiredInterceptor>> typeParameter : typeParameters) {
System.out.println(typeParameter.getName() + "变量类型注解信息:");
Annotation[] annotations = typeParameter.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
解析字段上的注解
1
2
3
4
5
6
7
@Test
public void m3() throws NoSuchFieldException {
Field nameField = Notification.class.getDeclaredField("id");
for (Annotation annotation : nameField.getAnnotations()) {
System.out.println(annotation);
}
}
解析泛型字段map上的注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void m4() throws NoSuchFieldException, ClassNotFoundException {
Field field = Notification.class.getDeclaredField("map");
Type genericType = field.getGenericType();
Type[] actualTypeArguments = ((ParameterizedType)
genericType).getActualTypeArguments();
AnnotatedType annotatedType = field.getAnnotatedType();
AnnotatedType[] annotatedActualTypeArguments = ((AnnotatedParameterizedType)
annotatedType).getAnnotatedActualTypeArguments();
int i = 0;
for (AnnotatedType actualTypeArgument : annotatedActualTypeArguments) {
Type actualTypeArgument1 = actualTypeArguments[i++];
System.out.println(actualTypeArgument1.getTypeName() + "类型上的注解如下:");
for (Annotation annotation : actualTypeArgument.getAnnotations()) {
System.out.println(annotation);
}
}
}
解析构造函数上的注解
1
2
3
4
5
6
7
@Test
public void m5() {
Constructor<?> constructor = Notification.class.getConstructors()[0];
for (Annotation annotation : constructor.getAnnotations()) {
System.out.println(annotation);
}
}
解析方法参数注解
1
2
3
4
5
6
7
8
9
10
11
@Test
public void m7() throws NoSuchMethodException {
Method method = Notification.class.getMethod("toString");
for (Parameter parameter : method.getParameters()) {
System.out.printf("参数%s上的注解如下:%n",
parameter.getName());
for (Annotation annotation : parameter.getAnnotations()) {
System.out.println(annotation);
}
}
}