← [ BACK_TO_NOTES ]

Java 注解处理概览

注解处理器如何在编译期读取注解并生成代码:与运行时反射的分工。

#concept #growing #dev #java

[!info] related notes

Java 注解处理概览

这个概念为什么出现

Dagger、MapStruct 等使用注解处理在 javac 期间生成源码或元数据、检查约束。Lombok 虽接入处理流程,但修改编译器语法树依赖编译器内部机制,不代表标准处理器都能改写现有源码。

[!abstract] 一句话理解 注解处理器在编译轮次中扫描注解并生成代码或报错;它不替代运行时反射,但可减少反射。

最小可观察示例

场景:理解保留策略决定谁能看见注解

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@interface Route { String value(); }

@Route("/health")
public class HealthController {
    public static void main(String[] args) {
        Route r = HealthController.class.getAnnotation(Route.class);
        System.out.println(r.value());
    }
}
javac HealthController.java && java HealthController

期望:

/health

说明:这是运行时可见注解演示;真正的处理器实现 Processor SPI,需先编译并由构建显式接入;独立模块是常用组织方式,不是 API 的强制要求。

结合场景再看三个关注点

  1. SOURCE 不写入 class 文件,CLASS 写入但不可经运行时反射读取,RUNTIME 保留到运行时;未声明 @Retention 默认 CLASS。源码编译中的处理器可读取三种保留策略的注解。
  2. 处理器在编译期执行;可通过 --processor-path 或 --processor-module-path 配置,并用 -processor 显式指定实现。
  3. 生成代码应可定位、可清理。

核心概念与准确模型

1. 处理器 API

javax.annotation.processing.Processor / AbstractProcessor

2. 轮次

处理器用 Filer 生成的新源码/类文件可进入后续轮次;不再生成新输入时进入最后一轮(processingOver())。标准 API 支持生成新文件和诊断,不提供任意改写已有类的能力。

3. 与反射分工

编译期处理运行时反射
时机javac运行
成本编译变慢运行开销
典型映射器生成框架扫描

边界情况与反直觉行为

  1. 增量编译与处理器兼容性。
  2. 生成代码与手写冲突。
  3. IDE 与命令行处理器路径不一致。

常见误区

[!warning] 常见误区:认为注解本身会“执行” 注解是元数据;行为来自处理器或运行时框架。

工程实践

  1. 处理器先编译,通常单独模块;显式配置处理器,避免依赖 classpath 自动发现的版本差异。
  2. 生成目录约定并 git 策略清晰。
  3. 失败信息指向用户源码位置。
  4. 版本化处理器与语言级别。

本节总结

  • 编译期元编程
  • 保留策略重要
  • 与反射互补

自测题

  1. SOURCE 保留的注解运行时能否反射到?
  2. 注解处理的主要收益?
参考答案
  1. 不能;SOURCE 注解不保留到 class 文件。编译期处理器生成的额外元数据是另一回事。
  2. 提前生成/校验,降低运行时反射与配置错误。

延伸阅读与资料来源

资料类型支撑内容
Annotation Processing文档(历史入口)概念
javax.annotation.processingAPI现代 API
创建于 2026/7/15 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本