Java基础:Java 注解(Annotation)及使用

Java 注解(Annotation)是 JDK 1.5 引入的特性,与类、接口、枚举是在同一等级。它可以作用在类、属性、方法、局部变量、方法参数上,用于对这些元素进行说明,注释,解释。

注解在功能上可以看成是一个接口,注解实例就是一个实现该接口的动态代理类,可在方便在程序运行期间通过反射获取该字段或方法的注解的实例,来决定下一步如何处理。

注解定义

注解 是一种标记,一种标注,一个极期相似的比喻就是标签纸,用于对其作用的目标对象进行说明,以便进行对应的操作。

有了注解,还需要对这些注解进行解析,可以通过反射机制(Reflect)访问这些注解并对其进行解析(注解解析类),没有解析的注解不起任何作用,没有任何意义的,但也不会影响代码的执行。

所有 注解 类型都继承 java.lang.annotation.Annotation 公共接口。注解的使用越来越流行,Spring Boot 框架就提供了大量的注解,由 SSM 框架的 XML 配置转为 Java 配置类,注解扮演很重要的角色,非常方便且代码更加简洁。

基本注解

定义注解与定义接口相似,只是在接口名前需要前使用 @ 符号。Java 5 内置了三种基本注解:

  • @Override:当前方法重写父类中的方法。
  • @Deprecated:标记该目标已被弃用,不建议使用,或存在更好的替代方案。
  • @SuppressWarnings:抑制编译器的警告信息。

Java 8 新增了一种注解:

  • @Repeatable:支持同一个注解可重复在同一类/方法/属性上使用,更低的版是不支持的。

元注解

元注解 :作用在注解上的注解,编译器自动会对这种类注解进行解析。 JDK 提供的元注解有 @Retention@Target@Documented@Inherited四个。

Spring 框架提供的很多注解,也使用了元注解。例如 Spring Boot 入口类的注解:

1
2
3
4
5
6
7
8
9
10
11
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//-----省略---------------
}

@Target

标记此类型的注解应该作用在那种 Java 成员上。该元注解只有一个属性,引用的是 java.lang.annotation.ElementType 枚举中的值。

  1. @Target

    1
    2
    3
    4
    5
    6
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
    ElementType[] value();
    }
  2. ElementType

    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
    public enum ElementType {
    /**
    * 类,接口,枚举声明
    */
    TYPE,
    /**
    * 字段声明(包括枚举值)
    */
    FIELD,
    /**
    * 方法声明
    */
    METHOD,
    /**
    * 方法参数声明
    */
    PARAMETER,
    /**
    * 构造方法声明
    */
    CONSTRUCTOR,
    /**
    * 局部变量声明
    */
    LOCAL_VARIABLE,
    /**
    * 注解类型声明
    */
    ANNOTATION_TYPE,
    /**
    * 包声明
    */
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
    }

@Retention

标记此类型的注解将保留多长时间(保留级别,或称为注解生命周期)。如果注解没有使用此元注解,则保留策略默认为 RetentionPolicy.CLASS

该元注解只有一个属性,引用的是 java.lang.annotation.RetentionPolicy 的枚举值。

  1. @Retention

    1
    2
    3
    4
    5
    6
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
    RetentionPolicy value();
    }
  2. RetentionPolicy

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public enum RetentionPolicy {
    /**
    * 只在源码中保留,将被编译器丢弃
    */
    SOURCE,

    /**
    * 编译时会记录到CLASS文件中,但运行时忽略,默认行为。
    */
    CLASS,

    /**
    * 编译时会记录到CLASS文件中,并在运行时由 JVM 保留,可以通过反射读取它们。
    * @see java.lang.reflect.AnnotatedElement
    */
    RUNTIME
    }

@Document

标记将注解包含在 Javadoc 中。指示默认情况下,javadoc 和类似工具将记录带有此类型的注解,成为公共API 的一部分。

类和方法的注解,在默认情况下是不出现在 javadoc 中的。如果使用 @Document 修饰该注解,则表示此注解会出现在 javadoc 中。

1
2
3
4
5
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Inherited

标记该注解是具有继承性的,使用该元注解的注解作用的目标类的子类具体父类的注解特性。只能作用在 Annotation 类型上。

1
2
3
4
5
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

注解作用

注解的作用大致可分为三类:

  • 生成文档:标识目标代码需要生成文档(doc)。
  • 代码分析:标识对代码进行解析,使用反射实现。
  • 编译检查:标识编译器可对代码进行最基本的编译检查。

注解使用

注解只有一个成员时,按规范写成 value(),当然不这么写也不会报错。如果不设置默认值,那么使用注解时必须要传值。只有类可以被注解,接口或抽象类不能被注解。

自定义注解

自定义一个对用户年龄范围进行校验的注解,实现用户年龄范围的控制和提示。

1
2
3
4
5
6
7
8
9
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AgeRange {

int max() default 120;

int min() default 0;
}

注解解析

  1. 定义一个用户类 User.class

    1
    2
    3
    4
    5
    6
    public class User {

    private String name;
    @AgeRange(max = 45, min = 18)
    private int age;
    }
  2. 对注解进行解析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public static void main(String[] args) {
    User user = new User().setAge(11);

    Class<User> userClass = User.class;
    Field[] fieldArray = userClass.getDeclaredFields();
    for (Field field : fieldArray) {
    AgeRange ageRange = field.getDeclaredAnnotation(AgeRange.class);

    if (ageRange != null) {
    if (user.getAge() > ageRange.max() || user.getAge() < ageRange.min())
    System.out.println("用户年龄必须在 18 到 45 之间");
    }
    }
    }

读取注解

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
//读取注解信息
public class ReadAnnotationInfoTest {
public static void main(String[] args) throws Exception {
// 测试AnnotationTest类,得到此类的类对象
Class c = Class.forName("com.iwtxokhtd.annotation.AnnotationTest");
// 获取该类所有声明的方法
Method[] methods = c.getDeclaredMethods();
// 声明注解集合
Annotation[] annotations;
// 遍历所有的方法得到各方法上面的注解信息
for (Method method : methods) {
// 获取每个方法上面所声明的所有注解信息
annotations = method.getDeclaredAnnotations();
// 再遍历所有的注解,打印其基本信息
System.out.println(method.getName());
for (Annotation an : annotations) {
System.out.println("方法名为:" + method.getName() + "其上面的注解为:" + an.annotationType().getSimpleName());
Method[] meths = an.annotationType().getDeclaredMethods();
// 遍历每个注解的所有变量
for (Method meth : meths) {
System.out.println("注解的变量名为:" + meth.getName());
}
}
}
}
}

其它参考

  1. Java 注解(Annotation)
  2. Java 8 注解 @Repeatable 使用技巧
  3. Java 注解 annotation
  4. Java 注解

Java基础:Java 注解(Annotation)及使用

http://blog.gxitsky.com/2019/09/21/Java-jdk-9-annotation/

作者

光星

发布于

2019-09-21

更新于

2022-06-17

许可协议

评论