Java里面随处可见annotation(注解),RetentionPolicy 指示了注解使用的情况:
- SOURCE,比如 @Override, @SuppressWarnings
- RUNTIME,最熟悉的莫过于Spring Bean中使用的 @Controller, @Service 一般和反射同时使用。
- CLASS
而 CLASS 则是用于 compile 编译阶段的注解。一个注解的处理器,以Java代码(或编译过的字节码)作为输入,生成Java文件。这些生成的Java文件,会同其他普通的手动编写的Java源代码一样被javac编译。
可以自己实现一些类似groovy语法糖的功能(lombok框架修改bytecode为类生成新方法getter/setter、或者使用生成新的辅助类等);减少机械的、冗余代码的管理,使得代码更简洁便于阅读。
代码生成
先来了解下整个过程,javac 从 ServiceLoader 获取一个 Processor 标注处理类,判断是否为符合条件的标注,再收集类的相关信息,然后使用 Filer 创建新的类。Java Annotation Processing and Creating a Builder ,java annotation processor 主要涉及到如下三部分:
- Annotation: @BuilderProperty
- Processor: BuilderProcessor
Service:
通过google的auto-service来注册服务,最终会在 META-INF/services/ 生成名称为 javax.annotation.processing.Processor 的文件,内容为当前被标注的类名。
项目的目录结构如下:
具体实现:
- BuilderProperty 注解
1 2 3 4 5 6 7 8 9 10 11 |
|
- BuilderProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
|
测试使用:
- build.gradle
我使用的是4.7的版本,4.7及以上版本可以直接使用 annotationProcessor 来添加标注处理器。(其他版本可以使用 apt 来处理)
1 2 3 4 5 6 7 8 9 10 |
|
- Person
这是一个POJO类,BuilderProcessor处理器会根据BuilderProperty注解来生成PersonBuilder类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
生成代码效果
在 gradle 面板中选择子项目 :example
,然后选择 Tasks 下的 build 任务进行构建。构建完后在 example/build/generated/source/apt
目录下生成了对应的 Builder 代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
注解处理器调试
不会调试说明还没有真正的入门。并且没有调试的情况下,解决异常、错误也是一件异常痛苦的事情。注解处理器生成代码是在编译阶段来生成代码的,所以调试的选项配置添加到 javac 。而 gradle 提供了一种相对简单的方式来进行。
参考
- how do you debug java annotation processors using intellij?
- How do you attach a debugger to gradle so that I can debug it running a task?
具体步骤如下:
在命令行运行构建
添加调试参数后,gradle 会 暂停等待远程调试 ,相当于添加了 JVM 调试参数。Gradle properties
hello-annotation-processor\example>gradle clean build --no-daemon -Dorg.gradle.debug=true 或者 hello-annotation-processor>gradle example:clean example:compileJava --no-daemon -Dorg.gradle.debug=true
注: –no-daemon 不加也是可以的,但是运行该次构建后不会停止。
远程调试
其他调试配置方式
通过环境变量
example>set GRADLE_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 example>gradle clean build Listening for transport dt_socket at address: 5005
修改 ~/.gradle/gradle.properties
这种方式不推荐,因为它是全局的。
org.gradle.daemon=false org.gradle.debug=true
或者
org.gradle.daemon=true org.gradle.jvmargs=-XX:MaxPermSize=4g -XX:+HeapDumpOnOutOfMemoryError -Xmx4g -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006 $ gradle --daemon
Then attach your debugger client to port 5006, set your breakpoint, then run your test.
注:该配置放到项目目录下没用。
其他
–END