一、元数据 metadata
元数据是指用来描述数据的数据,更通俗一点,就是描述代码间关系,或者代码与其他资源(例如数据库表)之间内在联系的数据。
基于元数据的广泛应用,JDK5.0 引入了 Annotation 的概念来描述元数据。在 Java 中,元数据以标签的形式存在于 Java 代码中,元数据标签的存在并不影响程序代码的编译和执行。
Java 语言中有四种类型(TYPE),即类(class)、枚举(enum)、接口(interface)和注解(@interface),它们是处在同一级别的。Java 就是通过注解来表示元数据的。
二、注解
注解相当于是一种嵌入在程序中的元数据,可以使用注解解析工具或编译器对其进行解析,也可以指定注解在编译期或运行期有效。
如果说注释是写给人看的,那么注解就是写给程序看的。它更像一个标签,贴在一个类、一个方法或者字段上。它的目的是为当前读取该注解的程序提供判断依据及少量附加信息。比如程序只要读到加了 @Test 的方法,就知道该方法是待测试方法,又比如 @Before 注解,程序看到这个注解,就知道该方法要放在 @Test 方法之前执行。有时我们还可以通过注解属性,为将来读取这个注解的程序提供必要的附加信息,比如 @RequestMapping("/user/info") 提供了 Controller 某个接口的 URL 路径。
注解三要素:定义、使用、读取并执行
Annotation 就是 Java 提供的一种元程序中的元素关联任何信息和任何元数据的途径。Annotation 是一个接口,程序可以通过反射来获取指定程序元素的 Annotation 对象,然后通过 Annotation 对象来获取注解里面的元数据。
java.lang.annotation.Annotation 本身是接口,而不是注解,当使用关键字 @interface 定义一个注解时,该注解隐式地继承了 java.lang.annotation.Annotation 接口;如果我们定义一个接口,并且让该接口继承自 Annotation,那么我们定义的接口依然是接口而不是注解。综上,定义注解只能依靠 @interface 实现。
注解分类
按照注解使用方法和用途分为 JDK 内置注解、第三方注解和自定义注解
按照注解参数个数分为
- 标记注解:没有成员变量的
Annotation类型称为标记注解 - 单值注解:只有一个值
- 完整注解:拥有多个值
JDK 内置注解
@Override:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。@Deprecated: 标记过时方法。如果使用该方法,会报编译警告。@SuppressWarnings:该注解的作用是阻止编译器发出某些警告信息。它可以有以下参数:
@SuppressWarnings("deprecation") // 过时的类或方法警告
@SuppressWarnings("unchecked") // 执行了未检查的转换时警告
@SuppressWarnings("fallthrough") // 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
@SuppressWarnings("path") // 在类路径、源文件路径等中有不存在的路径时的警告
@SuppressWarnings("serial") // 当在可序列化的类上缺少 serialVersionUID 定义时的警告
@SuppressWarnings("finally") // 任何 finally 子句不能完成时的警告
@SuppressWarnings("all") // 关于以上所有情况的警告
@Inherited:标记这个注解是继承于哪个注解类(默认注解并没有继承于任何子类)@SafeVarargs: 忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。@FunctionalInterface:标识一个匿名函数或函数式接口。@Repeatable:标识某注解可以在同一个声明上使用多次。
元注解
所谓元注解,就是加在注解上的注解。
@Documented:标记这些注解是否包含在用户文档中。@Target:它是被定义在一个注解类的前面,用来说明该注解可以被声明在哪些元素前。(默认可以放在任何元素之前)。它有以下参数:
ElementType.TYPE // 说明该注解只能被声明在一个类、接口、枚举前。
ElementType.FIELD // 说明该注解只能被声明在一个类的字段前。
ElementType.METHOD // 说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER // 说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR // 说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE // 说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE // 说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE // 说明该注解只能声明在一个包名前。
// 例如:此注解只能用在方法上
@Target(ElementType.METHOD)
@interface TestMethod {}
@Retention(注解的保留策略)
表示需要在什么级别保存该注释信息,用于描述注解的生命周期,即被描述的注解在什么范围内有效,表示注解类型保留时间的长短。
public @interface Retention {
RetentionPolicy value();
}
有三个备选值:SOURCE、ClASS、RUNTIME,保留时长递增
SOURCE:指定注解只保留在源文件当中。CLASS:指定注解只保留在 class 文件中。(缺省)RUNTIME:指定注解可以保留在程序运行期间。
// 此注解可以用于注解类、接口(包括注解类型) 或 enum 声明
@Target(ElementType.TYPE)
// 该注解运行时有效。注解处理器可以通过反射获取到该注解的属性值,从而去做一些运行时的逻辑处理
@Retention(RetentionPolicy.RUNTIME)
@interface TestRn {}
@Inherited
表明该注解将会被子类继承。加上该元注解的注解,只有用在类元素上才有效果。
// 被子类继承的注解
@Inherited
@interface TestInheri{}
自定义注解
@interface 用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum 以及它们的数组)。可以通过 default 来声明参数的默认值。
如果只有一个属性/方法且叫 value,那么使用该注解时可以不指定属性名,因为默认给 value 赋值,注解的属性如果有多个,无论是否叫 value,都必须写明属性的对应关系。
注解里面的每一个方法实际上就是声明了一个配置参数,其规则如下:
只能用
public或default这两个访问权修饰 ,默认为default注解参数只支持以下数据类型:基本数据类型、
String类型、Class类型、Enum类型、Annotation类型、以上类型的一维数组注解中的方法不能存在参数,可以包含默认值,使用
default来声明默认值。如果数组的元素只有一个,可以省略花括号 {}:
如果希望为注解的属性提供统一的几个可选值,可以使用常量类(另外定义静态类或者在注解内部定义静态类)
在程序中获取注解
// 返回指定注解,参数为注解类文件
<A extends Annotation> A getAnnotation(Class<A> annotationClass);
// 按从上到下的顺序返回所有注解
Annotation[] getAnnotations();