必须用RUNTIME:当依赖运行时反射读取注解(如@Component、@Test),需启动后扫描注册Bean或动态处理配置;CLASS适用于编译期工具链(如Lombok、Dagger);SOURCE仅用于编译器/IDE提示,零运行时开销。

选对@Retention策略,本质是在“能用”和“够快”之间做精准取舍。不是越晚丢弃越好,也不是越早清理越安全——关键看注解真正起作用的那个环节在哪里。
什么时候必须用 RUNTIME?
当你的逻辑依赖运行时反射读取注解信息时,RUNTIME 是唯一选择。比如 Spring 的 @Component、Jackson 的 @JsonProperty、JUnit 的 @Test,都靠 JVM 加载类后通过 getAnnotation() 动态识别行为。
- 需要在应用启动后扫描类/方法/字段并做处理(如自动注册 Bean、配置绑定)
- 注解内容会随外部输入动态变化(如从配置中心读取值再注入)
- 框架需在运行中多次调用
isAnnotationPresent()或getAnnotation()
CLASS 策略适合哪些真实场景?
CLASS 策略让注解留在 .class 文件里,但不进 JVM 堆内存。它专为编译后、运行前的工具链服务,是 APT、字节码增强、构建时校验的黄金分界点。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- Lombok 的
@Data、@Builder:注解在编译期被处理器解析,生成实际代码,运行时完全不需要 - Dagger/Hilt 的依赖图生成:APT 在编译时扫描
@Inject等注解,生成辅助类,运行时不反射 - 自定义构建检查:比如用 ASM 分析字节码,标记某些方法是否加了
@InternalApi
SOURCE 策略不只是“最轻”,更是“最干净”
SOURCE 注解连字节码都不进,纯粹是给编译器或 IDE 看的。它零运行时开销,也不参与任何构建流程,只在写代码时生效。
立即学习“Java免费学习笔记(深入)”;
-
@Override、@SuppressWarnings:编译器校验后立即丢弃,防止误重写或抑制警告 - 团队内部的开发提示注解:比如
@Todo("v2.0重构"),仅供 IDE 高亮,不希望它出现在生产包里 - 配合 Checkstyle 或 ErrorProne 编写自定义静态检查规则
别忽略默认值和性能代价
没写 @Retention 的注解,默认是 CLASS。这看似保守,但容易埋雷——你以为它只用于编译,结果某天有人想反射读取,发现读不到。
- RUNTIME 注解会让每个标注处都产生元数据对象,增加 class 文件体积、类加载时间、GC 压力
- 高频反射(如每请求遍历方法注解)可能成为性能瓶颈,建议缓存
Field/Method实例 - 如果只是想在测试环境启用某功能,可用 SOURCE + 编译开关,比 RUNTIME + 条件判断更彻底


















