Java 注解的解析有两种方式:反射和注解处理器(APT/JSR 269),本文主要介绍注解处理器解析方式。
- 解析方式
注解处理器解析注解的方式,是在编译期解析的,所以对注解的Rentention没有要求(即使RetentionPolicy.SOURCE也可以),可以根据编码需求自行设定。 - 工具属性
注解处理器是javac的一个工具,它用来在编译时扫描和处理对应注解。一个注解的注解处理器,通常以Java代码(或者编译过的字节码)作为输入,生成文件(通常是.java文件)作为输出。这些生成的Java代码是在新生成的.java文件中,所以不能修改已经存在的Java类,例如向已有的类中添加方法。这些生成的.java文件,会同其他普通的手动编写的Java源代码一样被javac编译。 - 独立进程
注解处理器是运行它自己的虚拟机JVM中的,javac启动一个完整Java虚拟机来运行注解处理器,所以可以把注解处理器库看做一个独立的Java项目。
自定义注解处理器流程
自定义注解和自定义注解处理器建议分成两个 jar 包,自定义注解处理器仅仅在编译期需要用到,所以工程中不需要将处理器打包到目标代码中。
自定义注解库 Custom Annotation
在 AS 中新建 Java 库:File --> New --> New Module --> Java Libary ,新建 myanno 注解库。
Gradle 文件:
1 | // Java 库 |
注解源文件:
1 | // 注解字段 |
Gradle 中选择该库,编译生成对应的 jar: myanno。
自定义注解处理器 Custom Processor
同样,需要新建一个自定义处理器 Java 库:processors。自定义注解处理器有两个步骤:
- 自定义注解处理器必须继承
AbstractProcessor - 库的资源文件夹中添加
javax.annotation.processing.Processor文件
继承 AbstractProcessor
自定义注解处理器必须继承 AbstractProcessor ,使用 @SupportedAnnotationTypes 定义支持解析的注解,并重写 process 处理对应注解。
1 | ({"com.ymzs.annotation.Field"}) |
添加 javax.annotation.processing.Processor 文件
为了将自定义注解处理器注册到 javac 时,必须打包一个特殊的文件 javax.annotation.processing.Processor 到 META-INF/services 目录下。原因见 Java 技术手册。
1 | // 源码目录结构 |
javax.annotation.processing.Processor 文件的内容是一个列表,每一行对应一个注解处理器的全称。例如:
1 | com.ymzs.parsing.AnnotationParsingProcessor |
Gradle 中选择该库,编译生成对应的 jar: processors。
注解处理器插件
在 AS 中使用自定义注解处理器需要添加对应的 gradle 插件:
- 纯
Java环境gradle推荐的注解处理器插件为net.ltgt.apt:Gradle 官网插件信息 ,插件的开源地址 gradle-apt-plugin, 使用方法参考: dagger, Annotation Processor in IntelliJ and Gradle 。
使用该插件时,被注解代码工程的gradle文件中需要包含如下信息,关键词annotationProcessor:
1 | plugins{ |
Android环境Google在发布AS时,开发了对应的android-gradle-plugin,这是一系列插件包,并包含注解处理器插件:Android 注解处理器插件使用方法 。
使用该插件时,被注解代码工程的gradle文件中需要包含如下信息,关键词annotationProcessor:
1 | // 使用 android-gradle-plugin 插件中的 application |
结果验证
在 Gradle Projects 窗口中,选择需要被注解代码工程,选择 Tasks --> build --> assemble 编译工程,能正确的打印自定义注解处理器中的 Log :
1 | // 代码工程编译期,解析注解 |
APT 和 JSR269
APTAPT: Annotation-Processing Tool,相关API都在com.sun.mirror包下。从Java 7开始就被降级了,在Java 8被彻底移除,APT 移除原因 。JSR269JSR 269 API: Pluggable Annotation Processing API,相关API都在javax.annotation.processing, javax.lang.model包下。从Java 6开始引入,后续版本逐步代替了APT。JSR 269
不管是那种 API ,它们都是处理注解的工具,对源代码文件进行检测,并找出对应注解后解析(或生成对应文件)。通常用来简化开发者的工作量,或者生成附属文件。虽然 APT 被移除了,但是因为历史原因,注解处理器还是常用 APT 来称呼。本文都是基于 JSR 269 API 实现的自定义注解处理器。
AnnotatedConstruct 体系
类图结构

AnnotatedConstruct 基本类
1 | public interface AnnotatedConstruct { |
TypeMirror 体系
1 | public enum TypeKind { |
Element 体系
1 | // 表示元素类别 |
源代码的每一个部分都是一个特定类型的 Element,也就是说 Element 代表程序的任意元素,例如包、类、方法等。每个 Element 代表一个静态的、语言级别的构件。
1 | package com.example; // PackageElement |
换个角度来看源代码,它只是结构化的文本,不是可运行的。可以想象它就像要被解析的 XML 文件一样(或者是编译器中抽象的语法树)。就像 XML 解释器一样,有一些类似 DOM 的元素,可以从一个元素导航到它的父或者子元素上。
举例来说,假如有一个代表 public class Foo 类的 TypeElement 元素,可以遍历它的所有元素:
1 | TypeElement fooClass = ... ; |
Elements 接口
用来处理 Element 的工具类,Elements JavaDoc 。
1 | public interface Elements { |
Types 接口
用来处理 TypeMirror 的工具类,Types JavaDoc 。
1 | public interface Types { |
Filer 接口
用来创建文件,Filer JavaDoc 。
1 | public interface Filer { |
Messager 接口
提供给注解处理器一个报告错误、警告以及提示信息的途径。Messager JavaDoc , 消息级别 Diagnostic.Kind 。
因为注解处理器是在独立的 java 虚拟机运行,所以注解处理器中不能直接进行异常抛出,否则进程异常崩溃时,会弹出一大堆让人捉摸不清的堆栈调用日志显示,也就是目标工程报的错误来自于另外一个虚拟机的堆栈。通常使用 Messager 来写一些信息给使用此注解器的第三方开发者的。在官方文档中描述了消息的不同级别,非常重要的是 Kind.ERROR,因为这种类型的信息用来表示我们的注解处理器处理失败了,很有可能是第三方开发者错误的使用了注解。
1 | // 打印信息 |
AbstractProcessor 详细分析
自定义处理器通常会继承 AbstractProcessor,并重写对应方法,来实现自定义注解的解析。所有的注解处理器类都必须有一个无参构造函数,否则执行时会报错。
对应源码文件目录:src\javax\annotation\processing
类图结构

常用 API 介绍
1 | public interface Processor { |
两个有用的注解
- @SupportedAnnotationTypes
表示当前类支持的注解的完整类路径,支持通配符。当前Processor要处理的Annotation名字全称。等同于getSupportedAnnotationTypes(),支持通配符,使用*表示支持所有注解。 - @SupportedSourceVersion
表示该处理器支持的源码版本。等同于getSupportedSourceVersion()。
循环调用 process
process 通常会被执行两次:根据 Log 可以发现,process 的返回值不管是 true/false 都会被执行两次?但是解析注解时,只有第一次执行 process 时,roundEnvironment.getElementsAnnotatedWith 才能获取到注解。
具体原因参考 Processor JavaDoc 。官方文档中第一段就介绍了,注解处理器是个循环处理的过程,每次循环都会解析上次处理器产生的源文件。也就是说第一次 process 处理了编写的源文件,第二次处理了注解处理器生成的源文件(此时已经不包含被处理的注解了),直到所有支持的注解都被解析完毕。
存在的问题
不管是使用 @SupportedAnnotationTypes 还是重写 getSupportedAnnotationTypes() ,只要支持注解包其中一个注解,注解处理器就能解析该包中的所有其他注解。
1 | // Java library: myanno |
注解包 myanno 包含三个注解,但是在自定义注解器中,代码中规定只支持 Field 注解,自定义注解器仍然能解析 Info, AnyAnnotation ?
注解处理器插件
gradle-apt-plugin
Java环境下注解插件,使用方法:1
2
3
4
5
6
7
8
9plugins{
id "net.ltgt.apt" version "0.15"
}
dependencies {
// 本地注解库
annotationProcessor project(":processors")
// 引用网络开源注解库
annotationProcessor "com.custom:CustomProcessors:version1.2.3"
}android-apt
Android注解器框架,现在已经不再维护。annotationProcessorAndroid Plugin for Gradle,google为gradle开发的注解处理器框架,用于替换android-apt。使用方法:1
2
3
4// 本地注解库
annotationProcessor ":customProcessors"
// 引用网络开源注解库
annotationProcessor "com.custom:CustomProcessors:version1.2.3"
常用开源库
AutoService官网
自动生成javax.annotation.processing.Processor文件,并将自定义注解处理器按照规范加入该文件。JavaPoet官网
生成.java源文件的API接口,非常强大实用。解析自定义注解时,可以使用JavaPoet来生成对应文件。
AutoService 用法
build.gradle文件中添加依赖implementation 'com.google.auto.service:auto-service:1.0-rc4'具体版本号,到github官网上查看。- 自定义注解处理器类上添加
AutoService注解
在自定义处理器类上,添加@AutoService(Processor.class)注解。该注解会自动生成对应的META-INF/services/javax.annotation.processing.Processor文件,并将自定义注解处理器按照规范加入该文件。
JavaPoet 用法
其他
调试注解处理器
常用场景
大量开源库都使用了注解处理器简化代码:
参考文档
- Java 注解处理器
- Annotation Processing 101
- AnnotatedConstruct 常用接口介绍
- 如何调试编译时注解处理器
- APT 移除原因
- JSR 269
- Annotation 原理到案例
- 自定义注解之编译时注解
- javax.annotation.processing.Processor 文件 Java 技术手册
- Gradle 官网 Java apt 插件信息
- gradle-apt-plugin
- Annotation Processor in IntelliJ and Gradle
- Android 注解处理器插件使用方法
- Android 打造编译时注解解析框架
- Android 注解使用之 annotationProcessor
- AutoService 官网
- JavaPoet 官网
- 几个注解处理器名称