@Retention(RetentionPolicy.SOURCE)的核心价值是将检查、约束或代码生成提前至编译期,不进入字节码、不占用运行时内存、不依赖反射,作用于编译器、IDE或APT工具。
@retention(retentionpolicy.source) 的核心价值,是把检查、约束或生成逻辑提前到编译期完成,不进字节码、不占运行时内存、也不依赖反射。
编译期注解的典型用途
SOURCE 注解不会出现在 .class 文件里,JVM 完全看不到它。它的作用对象是编译器本身、IDE 或 APT(Annotation Processing Tool)插件。
- 语法校验:如
@Override,编译器在解析子类方法时发现父类无对应签名,直接报错 - 参数取值限定:Android 中的
@IntDef、@StringDef,让 IDE 在调用处标出非法 int 值(如传了3而非预设的AAA=1或BBB=2) - 抑制警告:如
@SuppressWarnings("unchecked"),告诉 javac 别对某段代码发泛型擦除警告 - 触发代码生成:Lombok 的
@Data虽然最终效果在编译后体现,但其处理器就是靠 SOURCE 级注解驱动,在编译阶段生成 getter/setter 字节码
如何通过注解处理器(APT)处理 SOURCE 注解
Java 6+ 提供了标准的注解处理 API,你可编写一个 Processor 实现类,在 javac 编译过程中自动执行逻辑。
- 定义一个 SOURCE 级注解,比如:
@Retention(RetentionPolicy.SOURCE)<br>@Target(ElementType.METHOD)<br>public @interface LogOnCompile { } - 实现
javax.annotation.processing.Processor,重写process()方法,遍历所有被该注解标记的ExecutableElement - 在
process()中做你想做的事:打印方法名、生成辅助 Java 文件、校验命名规范、甚至抛出Messager.printMessage()编译错误 - 打包为 jar,并在
META-INF/services/javax.annotation.processing.Processor中声明全限定类名 - 编译时加上
-processorpath your-processor.jar -proc:only即可触发
IDE 和构建工具如何利用 SOURCE 注解
IntelliJ、Android Studio 等基于 Java PSI(Program Structure Interface)解析源码,它们并不依赖 JVM 运行时,而是直接读取 .java 文件 AST(抽象语法树)。
- 当你写
@IntDef({AAA, BBB})并用在参数上,IDE 在编辑时就能高亮非法字面量 - Gradle 的
compileJava任务默认启用 APT,只要 processor 在 classpath 或 annotationProcessor 配置中,就会自动调用 - 这种检查发生在
javac执行阶段,比单元测试早得多,属于“fail fast”设计
为什么不用 RUNTIME?什么时候必须用 SOURCE?
不是所有场景都适合运行时处理。有些需求天然排斥反射和运行时开销:
- 嵌入式或 Android 低内存设备:避免枚举带来的额外类加载和内存占用,改用
@IntDef+ int 常量 - 构建流水线强校验:要求 CI 编译失败而非运行时报错(例如禁止某包下出现
@Test) - 生成不可变结构:如 Protocol Buffers 编译器根据
.proto文件生成 Java 类,其内部注解策略也倾向 SOURCE 或 CLASS,不希望运行时还带一堆元数据 - 安全敏感逻辑:某些权限标记(如
@RequiresPermission)必须在调用前静态确认,不能留到运行时动态判断

















