注解
注解是在 JDK5 时引入的新特性,JDK5 内部提供了三个注解
- @Deprecated – 过时
- @Override – 重写
- @SuppressWarnings – 压制警告
1. 基本语法
1.1 注解声明
@Test 注解的声明:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Test {
}
- 使用了 @interface 声明了 Test 注解
- 使用 @Target 注解传入 ElementType.METHOD 参数来标明该注解只能用于方法上。
- 使用 @Retention(RetentionPolicy.RUNTIME) 用来表示该注解生存期是运行时。
从代码上看注解的定义很像接口的定义,确实如此,@Test 注解编译后也会生成 Test.class 文件。
1.2 元注解(标记其他注解的注解)
@Target 和 @Retention 是由 Java 提供的元注解,所谓元注解就是标记其他注解的注解。
-
@Target 用来约束注解可以应用的地方(如方法、类或字段),其中 ElementType 是枚举类型,其定义如下,也代表可能的取值范围:
public enum ElementType { TYPE, // 标明该注解可以用于类、接口(包括注解类型)或enum声明 FIELD, // 标明该注解可以用于字段(域)声明,包括enum实例 METHOD, // 标明该注解可以用于方法声明 PARAMETER, // 标明该注解可以用于参数声明 CONSTRUCTOR, // 标明注解可以用于构造函数声明 LOCAL_VARIABLE, // 标明注解可以用于局部变量声明 ANNOTATION_TYPE, // 标明注解可以用于注解声明(应用于另一个注解上) PACKAGE, // 标明注解可以用于包声明 TYPE_PARAMETER, // 标明注解可以用于类型参数声明(1.8新增) TYPE_USE // 类型使用声明 }
当注解未指定 Target 值时,则此注解可以用于任何元素之上,多个值使用{}包含并用逗号隔开:
@Target(value={CONSTRUCTOR, FIELD, METHOD, PARAMETER, TYPE})
-
@Retention用来约束注解的生命周期,其中 RetentionPolicy 是枚举类型,定义如下:
public enum RetentionPolicy { SOURCE, // 该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃 CLASS, // 该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中(当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等) RUNTIME // 注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等 }
JDK还提供了两个元注解:@Documented 和 @Inherited:
- @Documented 被修饰的注解会生成到 javadoc中。
- @Inherited 可以让注解被 ”继承“ ,但这并不是真的继承,只是通过使用 @Inherited,可以让子类对象使用getAnnotations() 方法获取到父类被 @Inherited 修饰的注解。
1.3 注解元素及其数据类型
内部没有定义其他元素的注解称为标记注解(marker annotation),例如 @Test 注解。一般定义注解时都会包含一些元素以表示某些值,方便处理器使用,例如 @Component 注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
与前面 @Test 注解不同的是,它声明一个 String 类型的 value 元素,其默认值为空字符。注意:注解中任何元素的声明应采用方法的声明方式,同时可选择使用 default 提供默认值。注解支持的元素数据类型如下:
- 所有基本类型(int,float,boolean,byte,double,char,long,short)
- String
- Class
- enum
- Annotation
- 上述类型的数组
声明注解元素时可以使用基本类型但不允许使用任何包装类型,同时还应该注意到注解也可以作为元素的类型(元素必须要么具有默认值,要么在使用注解时提供元素的值),也就是嵌套注解。使用示例:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Reference {
boolean next() default false;
}
enum Status {
FIXED,
NORMAL
}
@interface AnnotationElementDemo {
// 枚举类型
Status status() default Status.FIXED;
// 布尔类型
boolean showSupport() default false;
// String类型
String name() default "";
// class类型
Class<?> testCase() default Void.class;
// 注解嵌套
Reference reference() default @Reference(next=true);
// 数组类型
long[] value();
}
备注:
- 注解是不支持继承的,因此不能使用关键字 extends 来继承某个 @interface。
- 如果注解中定义了名为 value 的元素,在使用该注解时,如果该元素是唯一要赋值的一个元素,那么无需使用key = value 的语法,只需在括号内给出 value 元素所需的值即可,但是元素名必须为 value。例如 @Component 注解。
2. 注解与反射机制
Java 中所有注解都隐式继承了 Annotation 接口。java.lang.reflect 包下的 AnnotatedElement 接口则用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射读取注解信息,反射包的 Constructor 类、Field 类、Method 类、Package 和 Class 类等都实现了 AnnotatedElement 接口(注解都可以在这些元素上进行标注)。
AnnotatedElement 中相关的方法如下:
/** 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null */
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
/** 返回此元素上存在的所有注解,包括从父类继承的 */
Annotation[] getAnnotations();
/** 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false */
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
/** 返回直接存在于此元素上的所有注解,注意,不包括父类的注解, */
Annotation[] getDeclaredAnnotations();
3. JDK8 注解增强
元注解 @Repeatable 是 JDK8 新加入的,它表示在同一个位置可以重复相同的注解:
@FilterPath("/web/update")
@FilterPath("/web/add")
public class A {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterPaths {
FilterPath[] value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)
public @interface FilterPath {
String value();
}
可以简单理解为通过使用 @Repeatable 后,将使用 @FilterPaths 注解作为接收同一个类型上重复注解的容器,而每个 @FilterPath 则负责保存指定的路径串。