Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
以前,『XML』是各大框架的青睐者,它以松耦合的方式完成了框架中几乎所有的配置,但是随着项目越来越庞大,『XML』的内容也越来越复杂,维护成本变高。
于是就有人提出来一种标记式高耦合的配置方式,『注解』。方法上可以进行注解,类上也可以注解,字段属性上也可以注解,反正几乎需要配置的地方都可以进行注解。
关于『注解』和『XML』两种不同的配置模式,争论了好多年了,各有各的优劣,注解可以提供更大的便捷性,易于维护修改,但耦合度高,而 XML 相对于注解则是相反的。
追求低耦合就要抛弃高效率,追求效率必然会遇到耦合。
内置注解
Java 定义了一套注解,共有 7 个,3 个在java.lang
中,剩下 4 个在 java.lang.annotation
中。
- 作用在代码的注解是
@Override
- 只能标注方法,检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。@Deprecated
- 标记过时方法。如果使用该方法,会报编译警告。@SuppressWarnings
- 压制警告,@SuppressWarnings(value={"all"})
。
- 作用在其他注解的注解(或者说 元注解)是:
@Retention
- 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。@Documented
- 标记这些注解是否包含在用户文档中。@Target
- 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。@Inherited
- 只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。也就是A继承B同时也继承了标注@Inherited
的注解
- 从 Java 7 开始,额外添加了 3 个注解:
@SafeVarargs
- Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。@FunctionalInterface
- Java 8 开始支持,标识一个匿名函数或函数式接口。@Repeatable
- Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
Annotation 架构
1 个 Annotation 和 1 个 RetentionPolicy 关联。
1 个 Annotation 和 1~n 个 ElementType 关联。
Annotation.java
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
ElementType.java
package java.lang.annotation;
public enum ElementType {
TYPE, /* 类、接口(包括注释类型)或枚举声明 */
FIELD, /* 字段声明(包括枚举常量) */
METHOD, /* 方法声明 */
PARAMETER, /* 参数声明 */
CONSTRUCTOR, /* 构造方法声明 */
LOCAL_VARIABLE, /* 局部变量声明 */
ANNOTATION_TYPE, /* 注释类型声明 */
PACKAGE /* 包声明 */
}
RetentionPolicy.java
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 */
CLASS, /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */
RUNTIME /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
-
Annotation 就是个接口。
-
ElementType 是 Enum 枚举类型,它用来指定 Annotation 的类型,描述注解可以作用在哪些地方,可以对应多个。
-
RetentionPolicy 是 Enum 枚举类型,它用来指定 Annotation 的策略。就是指定注解的存活时间。
注解的本质就是一个接口。
public interface Annotation1 extends Annotation{
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@interface
意味着它实现了 java.lang.annotation.Annotation
接口,即该注解就是一个Annotation
。
定义 Annotation
时,@interface
是必须的。
注意:它和我们通常的 implemented
实现接口的方法不同。Annotation
接口的实现细节都由编译器完成。通过 @interface
定义注解后,该注解不能继承其他的注解或接口。
其实大部分内置注解我们都很熟悉了,这里不详细列举注解的使用了。
注解与反射
拟机规范定义了一系列和注解相关的属性表,也就是说,无论是字段、方法或是类本身,如果被注解修饰了,就可以被写进字节码文件。属性表有以下几种:
- RuntimeVisibleAnnotations:运行时可见的注解
- RuntimeInVisibleAnnotations:运行时不可见的注解
- RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解
- RuntimeInVisibleParameterAnnotations:运行时不可见的方法参数注解
- AnnotationDefault:注解类元素的默认值
对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。
- getAnnotation:返回指定的注解
- isAnnotationPresent:判定当前元素是否被指定注解修饰
- getAnnotations:返回所有的注解
- getDeclaredAnnotation:返回本元素的指定注解
- getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的
方法、字段中相关反射注解的方法基本是类似的,这里不再赘述。