
注解的保留策略和作用目标,决定了它“在哪起作用”和“能活多久”。这两个属性必须在定义注解时明确指定,否则编译器无法知道该注解该写在哪、要不要进字节码、运行时能不能拿到。
保留策略(RetentionPolicy):注解的生命周期
它回答的是“这个注解存在到哪个阶段”。Java 提供三种策略,用 @Retention 指定:
-
SOURCE:只保留在源代码里,编译完就丢掉。比如
@Override和 Lombok 的@Data就是这类——编译器用它做检查或生成代码,运行时完全看不到。 - CLASS:默认策略。注解会写进 .class 字节码文件,但 JVM 加载类时不会把它加载进内存,所以反射也拿不到。适合做构建工具(如 ProGuard)或静态分析使用的元数据。
-
RUNTIME:最“持久”的策略。注解既在源码中、也在 .class 文件里,还会被 JVM 加载进运行时环境。这样就能用反射(
clazz.getAnnotation(X.class))读取,Spring、JUnit 等框架依赖的就是这种。
作用目标(ElementType):注解能标注在哪
它回答的是“这个注解允许用在哪些语言元素上”。用 @Target 限定,常见取值有:
-
TYPE:类、接口、枚举、注解类型本身(即可以标在
public class A {}上) - FIELD:成员变量(包括 public/private/protected,不包括局部变量)
- METHOD:方法声明处(不是调用处)
- PARAMETER:方法或构造器的参数
- CONSTRUCTOR:构造方法
- LOCAL_VARIABLE:方法内部的局部变量(较少见,但合法)
- PACKAGE:包声明(需配合 package-info.java 使用)
一个注解可以支持多个目标,例如 @Target({ElementType.METHOD, ElementType.TYPE}) 表示既能标在类上,也能标在方法上。
组合使用才真正生效
单独设了 @Retention(RetentionPolicy.RUNTIME),不代表你就能在任意位置用它;同样,写了 @Target(ElementType.FIELD),但保留策略是 SOURCE,那运行时连字段上的注解都反射不到。实际开发中,典型搭配是:
- 框架级注解(如
@Service、@Test)→@Retention(RUNTIME) + @Target(TYPE/METHOD) - 编译检查类注解(如
@NonNull)→@Retention(SOURCE) + @Target(PARAMETER/FIELD) - 配置类注解(如某些构建时处理的标记)→
@Retention(CLASS) + @Target(TYPE)
没加 @Retention 或 @Target,编译器会按默认值处理(CLASS 和 TYPE),但显式写出更清晰、更安全。










