一、定义声明
在 Java
中通过 @interface
创建注解,并通过 @Target
与 @Retention
定义注解的作用对象与声明周期对象。
1. 作用对象
注解的作用对象表示该注解可作用于何种场景,即该注解是类注解亦或是方法注解等。
通过 @Target
注解定义注解的作用对象,其取值范围如下:
参数 | 描述 |
---|---|
ElementType.TYPE | 表明注解作用于类。 |
ElementType.CONSTRUCTOR | 表明注解作用于构造方法。 |
ElementType.FIELD | 表明注解作用于字段。 |
ElementType.METHOD | 表明注解作用于方法。 |
ElementType.PARAMETER | 表明注解作用于方法参数。 |
2. 作用范围
通过 @Retention
注解定义注解的声明周期,其取值范围如下:
参数 | 描述 |
---|---|
RetentionPolicy.CLASS | 默认的取值,表明作用于类文件。 |
RetentionPolicy.SOURCE | 表明注解作用编译期间。 |
RetentionPolicy.RUNTIME | 表明注解作用程序运行期间。 |
如下定义了一个方法注解,作用对象为方法,并设置作用于程序运行期间。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnn {
String value() default "";
String comment() default "";
}
3. 重复注解
默认注解不允许重复作用于同于一个目标,以上述的 @MethodAnn
为例,其无法在同一个方法上声明多次。
因此,在 JDK 8
中引入了新特性 @Repeatable
作用于注解使之允许重复作用于同一个对象。
还是以上述的 @MethodAnn
为例,因为涉及到重复注解所有需要先为定义容器注解:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnnGroup {
MethodAnn[] value() default {};
}
完成后修改之前定义的 @MethodAnn
注解,添加 @Repeatable
信息。
@Repeatable(MethodAnnGroup.class)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnn {
String value() default "";
String comment() default "";
}
此时 @MethodAnn
即可重复作用于同一个目标,使用方式如下:
public class TestClass {
@MethodAnnGroup({
@MethodAnn("test-method-message1")
@MethodAnn("test-method-message2")
})
public void method1() {
}
}
二、属性反射
在完成上述注解的定义与使用之后,即可通过反射获取不同注解中配置的属性值。
新建测试类 TestService.java
对三类注解进行简单应用供后续使用。
@ClassAnn(value = "test-class", comment = "test-class-comment")
public class TestService {
@FieldAnn("test-field1")
public String field1;
@FieldAnn("test-field2")
public String field2;
@MethodAnn("test-method1")
public void method1() {
}
@MethodAnn("test-method2")
public void method2(@ParameterAnn("abc") String msg1,
@ParameterAnn("def") String msg2) {
System.out.println(msg1 + msg2);
}
}
1. 类注解
在代码运行过程中,针对注解信息的获取通常由反射的方式从而实现。
下述为开发中常涉及到注解反射方法:
方法 | 作用 |
---|---|
isAnnotationPresent(class) | 判断字段是否存在注解。 |
getAnnotatedType() | 获取注解作用对象信息,包含字段类型与字段名等信息。 |
getAnnotations() | 获取字段上的所有注解,包含其父类中标识。 |
getAnnotation(class) | 通过注解类信息获取指定注解,包含其父类中标识。 |
getDeclaredAnnotations() | 获取字段上的所有注解,不包含其父类中标识。 |
getDeclaredAnnotation(class) | 通过注解类信息获取指定注解,不包含其父类中标识。 |
下述是一个简单的使用示例能够直观的看出各自方法的作用:
public void classDemo() {
Class<TestService> clazz = TestService.class;
Class<ClassAnn> annClazz = ClassAnn.class;
// Class is existed "ClassAnn"?
System.out.println("Exist annotation: " + clazz.isAnnotationPresent(annClazz));
// Get declare annotation
Annotation[] annotations = clazz.getDeclaredAnnotations();
System.out.println("Annotations: " + Arrays.toString(annotations));
// Get class annotation value
ClassAnn classAnn = clazz.getAnnotation(annClazz);
System.out.println("The annotation value: " + classAnn.value());
System.out.println("The annotation comment: " + classAnn.comment());
}
2. 字段注解
下述示例中通过反射获取所有字段列表,再由 getAnnotation()
获取字段注解并其配置值。
public void fieldDemo() {
Class<TestService> clazz = TestService.class;
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
boolean isPresent = f.isAnnotationPresent(fieldAnnClass);
System.out.println("Exist annotation? " + isPresent);
if (isPresent) {
FieldAnn fieldAnn = f.getAnnotation(FieldAnn.class);
System.out.println("Value: " + fieldAnn.value());
System.out.println("Comment: " + fieldAnn.comment());
}
}
}
3. 方法注解
方法注解的获取思路与字段注解获取类似,这里就不再描述直接提供代码示例。
public void methodDemo() {
Class<TestService> clazz = TestService.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
boolean isPresent = m.isAnnotationPresent(methodAnnClass);
System.out.println("Exist annotation? " + isPresent);
if (isPresent) {
MethodAnn methodAnn = m.getAnnotation(methodAnnClass);
System.out.println("Value: " + methodAnn.value());
System.out.println("Comment: " + methodAnn.comment());
}
}
}
4. 参数注解
方法中的参数注解获取对象较为复杂,通过反射返回的结果是一个二维数组 A[i][j]
,其中的 A[i][]
表示方法的第 i
个参数注解, A[][j]
存储的是对应注解的属性值。
参数注解的获取示例如下:
public class TestService {
public void method2(@ParameterAnn("abc") String msg1,
@ParameterAnn("def") String msg2) {
System.out.println(msg1 + msg2);
}
}
public void parameterDemo() throws NoSuchMethodException {
Class<TestService> clazz = TestService.class;
// 根据方法名和参数类型获取指定方法
Method method = clazz.getMethod("method2", String.class, String.class);
// Annotation[i][j] i: 方法的第 i 个参数注解, j: 该参数注解的第 j 个值
Annotation[][] annos = method.getParameterAnnotations();
Arrays.stream(annos).forEach(record -> {
ParameterAnn parameterAnn = null;
for (Annotation ann : record) {
if (ann instanceof ParameterAnn) {
parameterAnn = (ParameterAnn) ann;
}
}
System.out.print("\n" + parameterAnn);
System.out.print("Annotation value: " + parameterAnn.value());
System.out.print("Annotation comment: " + parameterAnn.comment());
});
}
最终打印输出的结果如下:
@xyz.ibudai.ann.ParameterAnn(value="abc", comment=""), Annotation value: abc, Annotation comment:
@xyz.ibudai.ann.ParameterAnn(value="def", comment=""), Annotation value: def, Annotation comment: