编写自定义注解需要了解一定的反射知识
元注解
作用在其他注解上的注解
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
- @Documented - 标记这些注解是否包含在用户文档中。
- @Target - 标记这个注解应该是哪种 Java 成员。
- @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
其中@Retention
和@Target
注解时必须存在
@Retention
@Retention注解声明了注解的作用时期
- SOURCE 编译器
- CLASS 编译之后,记录在class中,不会加载到JVM中
- RUNTIME 运行期,会加载到JVM中,可以通过反射获取到
@Target
@Target注解声明了注解的作用范围
- TYPE 类、接口、枚举声明
- FIELD 字段、枚举常量声明
- METHOD 方法声明
- PARAMETER 参数声明
- CONSTRUCTOR 构造函数声明
- LOCAL_VARIABLE 局部变量声明
- ANNOTATION_TYPE 注解声明
- PACKAGE 包声明
创建自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface TestAnnotation {
}
注解类型要写为@interface
由于我们使用自定义注解,都是需要在运行时获取注解进行使用,故@Retention使用RetentionPolicy.RUNTIME
@Target({ElementType.FIELD})
表示该注解只能声明在字段上
获取注解
创建一个测试类,在他的属性上声明注解
public class A {
@TestAnnotation
private String name;
private Integer age;
}
获取注解需要通过反射获取
TestAnnotation testAnnotation = A.class.getDeclaredField("name").getAnnotation(TestAnnotation.class);
如果变量testAnnotation
不为null,表示我们获取到了注解
也可以通过先判断,再获取的方式获取注解
Field nameField = A.class.getDeclaredField("name");
if (nameField.isAnnotationPresent(TestAnnotation.class)) {
TestAnnotation testAnnotation = nameField.getAnnotation(TestAnnotation.class);
}
给注解添加属性
注解的属性目前只支持几种类型
- 基本类型
- String
- Class
- 枚举
- 注解
- 以上任一类型的数组
非这几种类型会在编译期报错
官方文档说明:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6.1
https://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html
属性声明格式为: 类型+无参方法
方法名即为属性名
现在我们给我们的自定义注解添加一个名为value的属性
@Retention(value = RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface TestAnnotation {
String value();
}
此时会发现,我们的测试类中,注解下方出现错误红线,提示是value属性确实必填的值
如果希望value属性可以为空,可以为自定义注解的属性加上默认值,通过default
关键字来实现
@Retention(value = RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface TestAnnotation {
String value() default "";
}
加上default之后,测试类的错误就消失了,由此可见,注解的属性,必须有值,可以通过default关键字来进行默认值的处理
现在,我们可以在我们的自定义注解上,给属性value赋值了
public class A {
@TestAnnotation(value = "beijing")
private String name;
private Integer age;
// getter、setter、toString...
}
获取自定义注解属性
既然我们增加相关的属性,那么肯定的希望获取属性中的值的,那么我们可以在获取属性之后,调用对应的方法,即可获取到对应的属性值
Field nameField = A.class.getDeclaredField("name");
if (nameField.isAnnotationPresent(TestAnnotation.class)) {
TestAnnotation testAnnotation = nameField.getAnnotation(TestAnnotation.class);
String value = testAnnotation.value();
System.out.println(value); // beijing
}
比如我们想用注解的属性值替换实例的值
Class<A> clazz = A.class;
A a = clazz.newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(TestAnnotation.class)) {
TestAnnotation testAnnotation = field.getAnnotation(TestAnnotation.class);
field.setAccessible(true);
field.set(a, testAnnotation.value());
}
}
System.out.println(a); // A(name=beijing, age=null)
是不是就有点类似于Spring的@Value操作
至此基本的自定义注解就开发完了