RUNTIME是唯一支持反射读取的策略,因注解随类加载进入JVM元空间,SOURCE编译即丢、CLASS不加载进运行时内存,均无法被getAnnotation()等反射方法获取。

@Retention 必须显式指定为 RetentionPolicy.RUNTIME,注解才能在运行时通过反射读取并真正生效。
为什么 RUNTIME 是运行时生效的唯一选择
Java 的 @Retention 只有三个可选值,它们对应完全不同的生命周期阶段:
-
SOURCE:注解只保留在 .java 源文件中,编译后彻底消失。反射调用(如
method.getAnnotation())一定返回null; - CLASS:注解写入 .class 字节码,但 JVM 加载类时不加载进内存,反射 API 无法访问;
-
RUNTIME:注解完整保留在字节码中,并随类一起加载到 JVM 方法区,
getAnnotation()、isAnnotationPresent()等反射方法才能成功获取。
Spring 的 @Component、MyBatis 的 @Select、JUnit 的 @Test 全部依赖这个策略——没它,框架扫描和动态行为控制就无从谈起。
常见配置错误及后果
很多注解“写了却没反应”,根源就在 @Retention 配置不当:
立即学习“Java免费学习笔记(深入)”;
- 忘记写 @Retention,使用了默认的 CLASS 策略 → 反射拿不到注解,AOP 不触发、权限校验不执行;
- 误用 SOURCE 做运行时逻辑(比如想用注解控制日志开关)→ 启动后永远读不到值;
- 给仅用于文档或 IDE 提示的注解设成 RUNTIME → 白白增加类加载开销和内存占用。
正确写法与典型场景
标准声明格式如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) // ✅ 关键一行
public @interface LogExecution {
boolean value() default true;
}
适用 RUNTIME 的典型场景包括:
- 需要在运行期动态判断行为(如是否开启监控、是否跳过缓存);
- 被 Spring AOP、自定义 MethodInterceptor 等拦截处理;
- 参与序列化/反序列化规则(如
@JsonFormat); - 配合自定义注解处理器做运行时增强(非编译期生成代码)。
不是所有注解都需要 RUNTIME
按需选择才能兼顾功能与性能:
- 只用于编译检查或 IDE 提示(如
@NonNull)→ 用 SOURCE; - 供构建工具分析字节码(如埋点插桩、安全扫描)→ 用 CLASS;
- 既要在 Javadoc 中体现,又要在运行时生效 → 补上 @Documented + @Retention(RUNTIME)。


















