java注解处理器在编译期由javac调用,通过meta-inf/services/javax.annotation.processing.processor声明实现类,不依赖jpms的provides语法;其模块化应体现为职责分离的独立处理器jar,而非module-info声明。
java 中没有 provides 语法用于注解处理器模块化——这是常见误解。真正起作用的是 java 的 服务加载机制(serviceloader) 和 模块系统(jpms)中的 provides ... with 声明,但它仅适用于运行时服务发现,**不参与编译期注解处理流程**。
注解处理器本身不依赖 JPMS 模块声明
Java 注解处理器在编译阶段由 javac 调用,其注册方式是通过 META-INF/services/javax.annotation.processing.Processor 文件声明实现类全名,而非 module-info.java 中的 provides。即使你把处理器打包为 JPMS 模块,javac 也不会读取 module-info.class 来加载它。
关键点:
- 注解处理器必须以传统 JAR 形式提供,并在
META-INF/services/下正确声明入口类 -
provides java.util.ServiceLoader.Provider<processor> with MyProcessor</processor>在模块中写出来,对javac -processorpath无实际效果 - Lombok、MapStruct、AutoValue 等主流处理器均不使用 JPMS 模块描述,而是兼容 JDK 8+ 的扁平 classpath 模式
真正提升编译效率的模块化路径是分离与复用
所谓“模块化”,应理解为将注解处理器按职责拆分为独立可复用单元,再通过统一协调器组合,而非依赖 provides 语法。例如:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 定义
@DomainEntity注解,由entity-processor.jar处理:生成 JPA 实体 + DTO 映射 - 定义
@ApiContract注解,由api-processor.jar处理:生成 OpenAPI Schema + Retrofit 接口 - 两者互不耦合,可单独引入、测试、升级,避免“一个大而全的 processor”导致全量重编译
这种模块化直接缩短增量编译时间——改了 API 相关注解,entity-processor 根本不会触发扫描。
配合构建工具实现条件启用
在 Gradle 或 Maven 中,可通过 scope 控制哪些处理器参与编译:
- Kotlin 项目用
ksp替代kapt:KSP 是 Kotlin 原生注解处理框架,跳过 Java AST 解析,速度比 kapt 快 2–5 倍 - Java 项目用
annotationProcessor分组依赖,例如只在:domain模块中声明entity-processor,其他模块不感知 - 禁用非必要处理器:通过
-Xplugin:DisableAnnotationProcessing或 Gradle 的options.compilerArgs动态开关
Android 场景需额外注意 lint 注解协同
如果你在 Android 项目中同时使用 Jetpack 注解(如 @NonNull、@StringRes)和自定义注解处理器,二者可协同但不耦合:
- Jetpack 注解由
lint工具在 IDE 或 CI 阶段静态检查,不生成代码 - 你的注解处理器负责生成 Java/Kotlin 源码,例如从
@ViewModelFactory生成工厂类 - 两者共存时,建议将 lint 规则与处理器逻辑对齐(如
@ViewModelFactory要求构造函数参数非 null,则同步用@NonNull标记),提升错误定位精度










