Skip to content

注解解析

注解定义后,如果不读取它,就是一段无意义的代码。注解的价值在于被解析、被处理。这一节讲如何通过反射读取注解。

核心 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);
    }
}

注意事项

  1. Retention 必须是 RUNTIME:反射只能读取运行时注解
  2. getAnnotationsByType vs getAnnotations:JDK 8+ 处理重复注解用前者
  3. getDeclaredAnnotations:只获取自身声明的注解,不包含继承的
  4. 性能考虑:注解解析有反射开销,生产环境注意缓存结果

基于 VitePress 构建