概念
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
内置的注解
Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
作用在代码的注解是
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(或者说 元注解)是:
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
- @Documented - 标记这些注解是否包含在用户文档中。
- @Target - 标记这个注解应该是哪种 Java 成员。
- @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
从 Java 7 开始,额外添加了 3 个注解:
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
Annotation架构图
我们可以从图中得出以下信息:
- 每1个Annotation对象,都会有唯一的RetentionPolicy属性。
- 对于每 1 个 Annotation 对象,可以有若干个 ElementType 属性。
- Annotation 有许多实现类,每一个实现类,都 和 1 个 RetentionPolicy 关联并且和 1~n 个 ElementType 关联
自定义注解
使用元注解自己定义的注解
JDK中有一些元注解,主要有@Target,@Retention,@Document,@Inherited用来修饰注解。
@Target
表明该注解可以应用的Java元素类型
Target类型 | 描述 |
---|---|
ElementType.TYPE | 用于类、接口(包括注解类型)、枚举 |
ElementType.FIELD | 用于属性(包括枚举中的常量) |
ElementType.METHOD | 用于方法 |
ElementType.PARAMETER | 用于方法的形参 |
ElementType.CONSTRUCTOR | 用于构造函数 |
ElementType.LOCAL_VARIABLE | 用于局部变量 |
ElementType.ANNOTATION_TYPE | 用于注解类型 |
ElementType.PACKAGE | 用于包 |
ElementType.TYPE_PARAMETER | 1.8版本新增,用于类型变量 |
ElementType.TYPE_USE | 1.8版本新增,用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型) |
@Retention
表明该注解的生命周期
生命周期类型 | 描述 |
---|---|
RetentionPolicy.SOURCE | 编译时被丢弃,不包含在类文件中 |
RetentionPolicy.CLASS | JVM加载时被丢弃,包含在类文件中,默认值 |
RetentionPolicy.RUNTIME | 由JVM 加载,包含在类文件中,在运行时可以被获取到 |
@Document
表明该注解标记的元素可以被Javadoc 或类似的工具文档化
@Inherited
表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解
注解格式
/**
* 修饰符 @interface 注解名 {
* 注解元素的声明1
* 注解元素的声明2
* }
* 修饰符:访问修饰符必须为public,不写默认为pubic;
* 关键字:必须为@interface;
* 注解名: 注解名称为自定义注解的名称,使用时还会用到;
* 注解类型元素:注解类型元素是注解中内容,可以理解成自定义接口的实现部分;
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTestAnnotation {
/**
* 注解的元素声明的两种形式
* type elementName();
* type elementName() default value;
*/
String value() default "test";
}
注解的作用
- 编译检查
Annotation具有“让编译器进行编译检查的作用”。
例如,@SuppressWarnings, @Deprecated和@Override都具有编译检查作用。 - 在反射中使用Annotation
在反射的Class, Method, Field等函数中,有许多于Annotation相关的接口。
这也意味着,我们可以在反射中解析并使用Annotation。 - 根据Annotation生成帮助文档
通过给Annotation注解加上@Documented标签,能使该Annotation标签出现在javadoc中。 - 能够帮忙查看查看代码
通过@Override, @Deprecated等,我们能很方便的了解程序的大致结构。
另外,我们也可以通过自定义Annotation来实现一些功能。
5.注解处理器
如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。Java SE 5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。 - 在框架中的作用
在开发Java程序,尤其是Java EE应用的时候,总是免不了与各种配置文件打交道。以Java EE中典型的S(pring)S(truts)H(ibernate)架构来说,Spring、Struts和Hibernate这三个框架都有自己的XML格式的配置文件。这些配置文件需要与Java源代码保存同步,否则的话就可能出现错误。而且这些错误有可能到了运行时刻才被发现。把同一份信息保存在两个地方,总是个坏的主意。理想的情况是在一个地方维护这些信息就好了。其它部分所需的信息则通过自动的方式来生成。JDK 5中引入了源代码中的注解(annotation)这一机制。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。
因为注解大多都有自己的配置参数,而配置参数以名值对的方式出现,所以从某种角度来说,可以把注解看成是一个XML元素,该元素可以有不同的预定义的属性。而属性的值是可以在声明该元素的时候自行指定的。在代码中使用注解,就相当于把一部分元数据从XML文件移到了代码本身之中,在一个地方管理和维护。
上面两段话其实已经阐述了java注解的主要作用之一,就是跟踪代码依赖性,实现替代配置文件功能。比较常见的是Spring等框架中的基于注解配置。现在的框架很多都使用了这种方式来减少配置文件的数量。基本上秉持着这么一个原则,与具体场景相关的配置应该使用注解的方式与数据关联,与具体场景无关的配置放于配置文件中。在另一方面我们还可以在通过设置注解的@Retention 级别在运行时使用反射对不同的注解进行处理。
实现自定义注解
1.定义自定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
String value() default "test";
}
2.配置注解
@Data
@Builder
@MyAnnotation
public class MyBean {
private String name;
private Integer age;
}
3.利用反射解析注解
@Test
public void myAnnotationTest(){
try {
//获取MyBean的Class对象
MyBean myBean = MyBean.builder().build();
Class clazz = myBean.getClass();
//判断myBean对象上是否有MyAnnotation注解
if (clazz.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("MyBean类上配置了MyAnnotation注解!");
//获取该对象上MyAnnotation类型的注解
MyAnnotation myAnnotation = (MyAnnotation) clazz.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation.value());
} else {
System.out.println("MyBean类上没有配置MyTAnnotation注解!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
4.运行结果
MyBean类上配置了MyAnnotation注解!
test