前言
JavaPoet is a Java API for generating .java source files.
JavaPoet 是一个用于生成 .java 源文件的 Java API, 在许多如 ButterKnift、ARoute 等使用了 APT 技术的开源库中,都可以看到 JavaPoet 的影子。在解析注解生成 Java 源文件的过程中,JavaPoet 起的作用很大。

上图是 JavaPoet 库的文件结构:
AnnotationSpec用于生成注解相关的 API;FieldSpec用于生成属性变量字段相关的 API;MethodSpec用于生成类的构造器、或者方法相关的 API;ParameterSpec用于生成方法参数相关的 API;TypeSpec用于生成class类、接口、或者enum相关的 API;className用于声明在.java文件中通过import关键字导入
例子
在 Android Studio 中新建一个 Java 库,然后在 .gradle 文件中添加 JavaPoet 的依赖,效果如下:

在了解 JavaPoet 生成 .java 文件的规则之前,有必要先了解 JavaPoet 的字符串格式化规则:
| 符号 | 意义 |
|---|---|
$L |
表示字面量,如: int value = $L |
$S |
表示字符串 |
$T |
可以表示类、接口 |
$N |
表示变量、函数名,和 $S 区分,主要是 $N 有实际的意义 |
# 例子_1
如下,如果我们知道我们要生成的类格式如下:
1 | package com.example.helloworld; |
通过 JavaPoet 库,我们可以通过如下 API :
1 | // 生成 main 方法 |
效果如下:

# 例子_2
1 | void main() { |
可以通过 JavaPoet 生成:
1 | MethodSpec mainMethod = MethodSpec.methodBuilder("main") |
对于上面的 beginControlFlow()、nextControlFlow()、endControlFlow(), 一般可以用在循环、异常捕获、条件判断等。注意,在循环、异常捕获、条件判断的花括号最后要加上 endControlFlow()。
# 例子_3
ClassName类型它可以标识任何声明的类。声明的类型只是Java丰富类型系统的开始:我们还具有数组,参数化类型,通配符类型和类型变量。JavaPoet具有用于构建以下每个类的类:
1 | ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard"); |
生成的 .java 文件如下:
1 | package com.example.helloworld; |
# 例子_4
JavaPoet支持导入静态。它通过显式收集类型成员名称来实现。
1 | ... |
生成的 .java 文件如下:
1 | package com.example.helloworld; |
# $N
生成的代码通常是自引用的。使用 $N 通过其名称引用另一个生成的声明。这是一个调用另一个的方法:
1 | public String byteToHex(int b) { |
在生成上面的代码时,我们使用 $N 将 hexDigit() 方法作为参数传递给 byteToHex() 方法:
1 | MethodSpec hexDigit = MethodSpec.methodBuilder("hexDigit") |
# 构造函数
使用 MethodSpec 也可以生成类的构造函数。
1 | MethodSpec flux = MethodSpec.constructorBuilder() |
生成的代码如下:
1 | public class HelloWorld { |
# 参数
使用 ParameterSpec.builder() 或 MethodSpec 的 API addParameter() 在方法和构造函数上声明参数:
1 |
|
生成的代码如下:
1 | void welcomeOverlords(final String android, final String robot) { |
# 字段
使用 FieldSpec#builder() 或 TypeSpec#addField() 生成字段。
1 | FieldSpec android = FieldSpec.builder(String.class, "android") |
生成的代码如下:
1 | public class HelloWorld { |
# 接口
对于 JavaPoet 的接口,请注意,接口方法必须始终为 PUBLIC ABSTRACT,并且接口字段必须始终为 PUBLIC STATIC FINAL。定义接口时,必须使用以下修饰符:
1 | TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld") |
但是,在生成代码时将省略这些修饰符。
1 | public interface HelloWorld { |
# 枚举
1 | TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") |
生成的代码如下:
1 | public enum Roshambo { |
小结
JavaPoet 还有挺多细节的生成规则,建议感兴趣的小伙伴可以阅读 JavaPoet 的官方文档。