0%

java使用自定义注解

注解简介

注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”,用于保存类相关的信息以供反射调用,提供了一种为程序元素提供元数据的方法。注解不能直接干扰程序的运行。注解本身也是一个类,所有的注解都需要继承自java.lang.annotation.Annotation接口。

注解功能

注解的作用主要包括

  • 作为特定的标记,告诉编译器一些信息
    例如Override注解,编译器会判断是否重写了父类的某个方法,如果没重写,则会编译错误。
  • 编译时动态处理,如动态生成代码
    例如Lombok的Data,Getter,Setter注解,可以在编译时添加方法。
  • 运行时动态处理,作为额外信息的载体

元注解

元注解用于修饰注解的注解,用于注解的定义上。包含Retention,Target,Inherited,Documented四个注解。在使用自定义注解前,需要先了解元注解的意义。

Retention

Retention标记了注解的生命周期。其源码如下,可以看到value为RetentionPolicy类型

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}

RetentionPolicy的源码如下,定义了注解的不同的生命周期。

1
2
3
4
5
6
7
8
public enum RetentionPolicy {
/* 注解在源文件保留,在编译后的class文件删除*/
SOURCE,
/* 在class文件中保留,运行时获得*/
CLASS,
/* 在运行时可通过反射获得*/
RUNTIME
}

Target

Target标记了注解的适用范围。其源码如下,可以看到value为ElementType类型的数组。

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}

其中ElementType的源码如下,在这之中定义了注解可以用在的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public enum ElementType {
/** 类,接口,枚举类 */
TYPE,
/** 字段 */
FIELD,
/** 方法 */
METHOD,
/** 方法参数 */
PARAMETER,
/** 构造方法 */
CONSTRUCTOR,
/** 本地变量 */
LOCAL_VARIABLE,
/** 注解 */
ANNOTATION_TYPE,
/** 包 */
PACKAGE,
/** 泛型参数 java8以后 */
TYPE_PARAMETER,
/** 任何类型 java8以后 */
TYPE_USE,
/** 模块 java9以后 */
MODULE
}

Inherited

标识注解是否应当是否允许子类继承

Documented

标识注解是否应当被包含在JavaDoc文档中。

自定义注解

java使用@interface方式定义注解,自定义注解的方法如下。在自定义的过程中需要注意,自定义注解的属性的访问控制符必须是public,同时属性一定需要加括号。

1
2
3
4
5
6
7
8
9
10
11
12
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Target({ElementType.TYPE,ElementType.FIELD})
public @interface MyAnnotation{
//错误,访问控制符必须为public
//private String name() default "test";
//错误,需要加括号
//public String name default "test";
String name() default "test";
int id();
}

处理自定义注解

在JAVA中主要使用AnnotatedElement接口定义了如何获取类的信息。其中isAnnotationPresent方法可以用于判断是否被指定的注解是否存在,getAnnotations方法获取所有的注解,getAnnotation获取指定的注解。演示代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@MyAnnotation(type = "class")
public class AnnotationTest {

@MyAnnotation(type = "field")
public Object object = new Object();

public static void main(String[] args) {
Class<AnnotationTest> clazz = AnnotationTest.class;
// 解析类的注解
//获取所有的注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof MyAnnotation) {
System.out.println(((MyAnnotation) annotation).type());
}
}

// 解析属性的注解
Field field = clazz.getDeclaredFields()[0];
//判断注解是否存在
if (field.isAnnotationPresent(MyAnnotation.class)) {
//获取指定的注解
MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation.type());
}
}
}

在上述语句中,如果将MyAnnotation的Retention值设为RUNTIME,那么将输出

1
2
class
field

但是如果将Retention值设为CLASS或者SOURCE,由于在执行时已经没有了注解相关的信息,那么将会没有输出。

参考资料

使用注解