1、常见元注解
1.1 @target
源码:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value(); }
作用:用于指定被注解的注解能注解于哪些 java 元素上(如:类、属性、方法等),类型为枚举ElementType[]
(关于枚举ElementType
的具体取值,大家可自行查看源码)。在不指定具体ElementType
时,表示被注解的注解可注解于任何 java 元素。此时,会根据被注解的注解所注解的类型自适应具体的ElementType
。
示例:
@Target({ElementType.TYPE, ElementType.FIELD})// --------A @interface MyAnnotation { } @MyAnnotation// ----------------------B class TestAnnotation { @MyAnnotation// ------------------D private Integer id; @MyAnnotation// ------------------D public void print() { } }
A处指定ElementType
为TYPE
和FIELD
,表示@MyAnnotation
可用于注解类和属性,因此,B、C 处编译通过,而 D 处编译报错。
若将 A 删除,则 B、C、D 三处都编译通过,因为@MyAnnotation
的@Target
会根据@MyAnnotation
所注解的类型自适应具体的ElementType
。
1.2 @Retention
源码:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); }
作用:指定被注解的注解的生命周期,类型为枚举RetentionPolicy
,有三个值可选:SOURCE
、CLASS
、RUNTIME
。当不指定RetentionPolicy
时,默认为CLASS
。
值说明:
SOURCE
:表示被注解的注解只保留在 java 源文件中(后缀是.java
的文件),JVM不会进行编译;CLASS
:表示被注解的注解能被编译到 class 文件中(后缀是.class
的文件,即 class字节码文件),但JVM会忽略,即无法获取;RUNTIME
:表示被注解的注解能被编译到JVM中,可通过反射获取,实际开发中的自定义注解的生命周期几乎都是这个。
1.3 @Documented
源码:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
作用:指定被注解的注解能跟随 java 文件到JavaDoc文档中。
1.4 @Inherited
源码:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
作用:指定被注解的注解所注解的类的子类是否可继承此被注解的注解。
示例。
情形一:(没有定义@Inherited
)
@Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { } @MyAnnotation class Person { } class Teacher extends Person { } public class TestAnnotation { public static void main(String[] args) throws Exception { Class personClass = Class.forName("com.neusoft.boot.Person");// 使用反射,通过全限定名获取Class对象 Class teacherClass = Class.forName("com.neusoft.boot.Teacher"); MyAnnotation a1 = (MyAnnotation) personClass.getAnnotation(MyAnnotation.class);// 获取注解在类Person上的@MyAnnotation sout a1;// 打印:@com.neusolt.boot.MyAnnotation() MyAnnotation a2 = (MyAnnotation) teacherClass.getAnnotation(MyAnnotation.class);// 获取注解在类Teacher上的@MyAnnotation sout a2;// 打印:null } }
情形二:
@Inherited @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { } @MyAnnotation class Person { } class Teacher extends Person { } public class TestAnnotation { public static void main(String[] args) throws Exception { Class personClass = Class.forName("com.neusoft.boot.Person"); Class teacherClass = Class.forName("com.neusoft.boot.Teacher"); MyAnnotation a1 = (MyAnnotation) personClass.getAnnotation(MyAnnotation.class); sout a1;// 打印:@com.neusolt.boot.MyAnnotation() MyAnnotation a2 = (MyAnnotation) teacherClass.getAnnotation(MyAnnotation.class); sout a2;// 打印:@com.neusolt.boot.MyAnnotation() } }
2、自定义注解
上文中常见元注解的示例都是基于自定义注解,相信大家对自定义注解已有了初步了解。
2.1 概述
所有注解都自动继承java.lang.annotation.Annotation
接口,注解由@interface
声明,上述源码中的value()
是注解元素,类似于成员属性。
注解元素必须由()
结尾,可以使用default
定义默认值,使用注解时必须为所有无默认值的注解元素赋值。
举个例:
@Target(ElementType.TYPE) @Inherited @Retention(RetentionPolicy.RUNTIME) @Documented @interface MyAnnotation { String name() default "cs"; } @MyAnnotation(name = "csdn")// 这里的 (name = "csdn") 可省略 class Person { }
注解@MyAnnotation
有一个注解元素name
,默认值为"cs"
。由于name
有默认值,故使用此注解时不必须为name
赋值。
2.2 注意事项
- 注解元素必须由
public
修饰,默认是public
; - 一般注解元素以名词命名,若只有一个,建议名称为
value
; - 注解元素类型只能是基本数据类型、基本数据类型数组或注解类型(注解嵌套);
default
指定注解元素默认值时,值类型必须与注解元素类型相同。
2.3 使用细节
- 若无注解元素,可省略
()
(小括号); - 若注解元素类型为数组,且赋值时只有一个值时,可以省略
{}
(花括号); - 如果只有一个注解元素,且注解元素名为
value
。无论其是什么类型,都可省略前缀。如上述例子:需要name = "csdn"
为注解元素name
赋值,如果注解元素名为value
,则可省略“value =
”; - 若
ElementType
为PACKAGE
,则此注解用于注解在package-info.java
文件中(PS:这个文件默认是不创建的。在idea中,双击shift可搜索到,打开时才会创建。其用途尚未可知,欢迎大家留言!),而不是类文件(xx.java)的第一行的package...
上。
4、最后
本文中的示例是为了方便大家理解自定义注解的定义和使用方法而简单举出的,不一定有实用性。大家在看完本篇文章后,可能仍有疑惑,不妨自行测试一下,就都理解了。
本文完结。