注解解析
注解定义后,如果不读取它,就是一段无意义的代码。注解的价值在于被解析、被处理。这一节讲如何通过反射读取注解。
核心 API
| 方法 | 说明 |
|---|---|
isAnnotationPresent(Class) | 判断是否存在指定注解 |
getAnnotation(Class) | 获取指定注解(不存在返回 null) |
getAnnotations() | 获取所有注解(包含继承的) |
getDeclaredAnnotation(Class) | 获取自身声明的注解(不含继承) |
getAnnotationsByType(Class) | JDK 8+,获取重复注解 |
代码示例
获取类注解
java
import java.lang.annotation.*;
@Deprecated
class OldClass {
void oldMethod() {}
}
public class ClassAnnotationDemo {
public static void main(String[] args) {
// 检查类是否有指定注解
if (OldClass.class.isAnnotationPresent(Deprecated.class)) {
Deprecated deprecated = OldClass.class.getAnnotation(Deprecated.class);
System.out.println("Class is deprecated");
}
}
}获取方法注解
java
import java.lang.annotation.*;
import java.lang.reflect.*;
@Retention(RetentionPolicy.RUNTIME)
@interface Author {
String name();
String date();
}
public class MethodAnnotationDemo {
@Author(name = "张三", date = "2024-01-01")
public void importantMethod() {}
public static void main(String[] args) throws Exception {
Method method = MethodAnnotationDemo.class.getMethod("importantMethod");
if (method.isAnnotationPresent(Author.class)) {
Author author = method.getAnnotation(Author.class);
System.out.println("Author: " + author.name());
System.out.println("Date: " + author.date());
}
}
}解析字段注解
实际应用中最常见的场景:ORM 框架读取字段注解生成 SQL。
java
import java.lang.annotation.*;
import java.lang.reflect.*;
@Retention(RetentionPolicy.RUNTIME)
@interface Column {
String name();
int length() default 255;
boolean primaryKey() default false;
}
public class FieldAnnotationDemo {
static class User {
@Column(name = "user_id", primaryKey = true)
private int id;
@Column(name = "user_name", length = 100)
private String name;
@Column(name = "user_email")
private String email;
}
public static void main(String[] args) {
// 通过反射读取字段注解,构建 SQL
StringBuilder sql = new StringBuilder("SELECT ");
for (Field field : User.class.getDeclaredFields()) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
sql.append(column.name()).append(", ");
System.out.println("Field: " + field.getName() +
" -> Column: " + column.name() +
" (length: " + column.length() + ")");
}
}
sql.append(" FROM user");
System.out.println(sql);
}
}解析参数注解
java
import java.lang.annotation.*;
import java.lang.reflect.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface NotNull {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface Min {
int value();
}
public class ParameterAnnotationDemo {
public void process(@NotNull String input, @Min(0) int count) {
System.out.println(input + ", " + count);
}
public static void main(String[] args) throws Exception {
Method method = ParameterAnnotationDemo.class.getMethod("process", String.class, int.class);
Parameter[] params = method.getParameters();
for (Parameter param : params) {
System.out.print("Parameter: " + param.getName());
if (param.isAnnotationPresent(NotNull.class)) {
System.out.print(" [NotNull]");
}
if (param.isAnnotationPresent(Min.class)) {
Min min = param.getAnnotation(Min.class);
System.out.print(" [Min: " + min.value() + "]");
}
System.out.println();
}
}
}处理重复注解(JDK 8+)
java
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Authors.class)
@interface Author {
String name();
}
@Retention(RetentionPolicy.RUNTIME)
@interface Authors {
Author[] value();
}
public class RepeatableAnnotationDemo {
@Author(name = "张三")
@Author(name = "李四")
@Author(name = "王五")
public void annotatedMethod() {}
public static void main(String[] args) throws Exception {
Method method = RepeatableAnnotationDemo.class.getMethod("annotatedMethod");
// JDK 8+ 推荐方式
Author[] authors = method.getAnnotationsByType(Author.class);
for (Author author : authors) {
System.out.println("Author: " + author.name());
}
}
}注解处理器框架
把以上技术组合起来,可以写一个简化版的 JUnit:
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test {
int priority() default 0;
}
public class SimpleTestRunner {
public static void run(Class<?> clazz) {
System.out.println("Running tests in " + clazz.getSimpleName());
// 按优先级排序执行
List<Method> tests = Arrays.stream(clazz.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(Test.class))
.sorted((m1, m2) -> {
int p1 = m1.getAnnotation(Test.class).priority();
int p2 = m2.getAnnotation(Test.class).priority();
return Integer.compare(p1, p2);
})
.toList();
for (Method test : tests) {
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
test.invoke(instance);
System.out.println(" ✅ " + test.getName());
} catch (Exception e) {
System.out.println(" ❌ " + test.getName() + " - " + e.getCause());
}
}
}
// 使用示例
public static class MyTest {
@Test(priority = 2)
public void testSecond() { System.out.println("Second test"); }
@Test(priority = 1)
public void testFirst() { System.out.println("First test"); }
}
public static void main(String[] args) {
run(MyTest.class);
}
}注意事项
- Retention 必须是 RUNTIME:反射只能读取运行时注解
- getAnnotationsByType vs getAnnotations:JDK 8+ 处理重复注解用前者
- getDeclaredAnnotations:只获取自身声明的注解,不包含继承的
- 性能考虑:注解解析有反射开销,生产环境注意缓存结果
