497. Java 反射 - 使用反射读取注解
1. 为什么要关心注解?
在现代 Java 开发中,注解已经成为框架和库的“开关”。
- ORM 框架(如 Hibernate、JPA):用注解标记实体字段和表的映射。
- Spring:用注解实现依赖注入、事务管理、安全控制。
- 验证框架:用注解标记参数是否允许
null、是否必须符合某种格式。
👉 注解之所以能发挥作用,核心原因就是:运行时通过反射 API 读取注解并执行相应逻辑。
2. 获取注解的工具类:AnnotatedElement
以下几个反射类都实现了 AnnotatedElement 接口:
-
Class(类、接口、枚举、记录、数组) -
Field(字段) -
Method(方法) -
Constructor(构造函数)
它们提供了几组关键方法:
-
isAnnotationPresent(Class<?>):是否存在某个注解。 -
getAnnotations():获取该元素上的所有注解(包括继承的)。 -
getDeclaredAnnotations():只获取该元素本身声明的注解。 -
getAnnotation(Class<?>):获取指定类型的注解实例。 -
getAnnotationsByType(Class<?>):获取重复注解。
3. 示例:类级别注解
定义枚举和注解:
enum SerializedFormat { BINARY, XML, JSON }
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Bean {}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Serialized {
SerializedFormat format() default SerializedFormat.JSON;
}
在类上使用:
@Serialized
@Bean
public class Person {}
通过反射读取:
Class<?> c = Person.class;
boolean isBean = c.isAnnotationPresent(Bean.class);
System.out.println("isBean = " + isBean);
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("annotation = " + annotation);
}
输出:
isBean = true
annotation = @org.devjava.Serialized(format=JSON)
annotation = @org.devjava.Bean()
👉 注意:返回的其实是注解类的实例对象,你可以直接调用它的方法。
Serialized serialized = c.getAnnotation(Serialized.class);
System.out.println("format = " + serialized.format());
输出:
format = JSON
4. 示例:重复注解 (Repeatable Annotations)
定义验证规则:
enum ValidationRules { NON_NULL, NON_EMPTY, NON_ZERO }
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Validators {
Validator[] value();
}
@Target(ElementType.FIELD)
@Repeatable(Validators.class)
@interface Validator {
ValidationRules value();
}
应用在 Person 类的字段上:
public class Person {
@Validator(ValidationRules.NON_NULL)
@Validator(ValidationRules.NON_EMPTY)
private String name;
}
读取注解:
方式一:通过容器注解 @Validators
Field nameField = Person.class.getDeclaredField("name");
Annotation[] annotations = nameField.getAnnotations();
Validators validators = (Validators) annotations[0];
for (Validator v : validators.value()) {
System.out.println("validator = " + v);
}
输出:
validator = @org.devjava.Validator(NON_NULL)
validator = @org.devjava.Validator(NON_EMPTY)
方式二:直接用 getAnnotationsByType()
Validator[] validators = nameField.getAnnotationsByType(Validator.class);
for (Validator v : validators) {
System.out.println("annotation = " + v);
}
输出:
annotation = @org.devjava.Validator(NON_NULL)
annotation = @org.devjava.Validator(NON_EMPTY)
👉 第二种方式更简洁,JDK 会自动帮你展开容器注解。
5. 总结
- 注解是框架的“说明书”,框架通过反射读取注解来决定如何运行。
-
类、方法、字段、构造函数都可以携带注解,并通过
AnnotatedElement访问。 -
isAnnotationPresent():检查是否存在。 -
getAnnotation():获取单个注解。 -
getAnnotationsByType():用于重复注解。