这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
JavaPoet
JavaPoet是Square推出的开源java代码生成框架,提供Java Api生成Java源文件。在注解处理器中,能很方便的生成想要的代码。
JavaPoet常用类
| 类对象 | 说明 |
|---|---|
| MethodSpec | 代表一个构造函数或方法声明 |
| TypeSpec | 代表一个类,接口,或者枚举声明 |
| FieldSpec | 代表一个成员变量,一个字段声明 |
| JavaFile | 包含一个顶级类的Java文件 |
| ParameterSpec | 用来创建参数 |
| AnnotationSpec | 用来创建注解 |
| ClassName | 用来包装一个类 |
| TypeName | 类型,如返回值类型 |
JavaPoe字符串格式化规则
$L字面量,如:int value = $L,10$S字符串,如:$S,hello$T类、接口,如:$T,MainActivity$N变量,如:user.$N,name
HelloWorld
看看官网Demo是怎么写的
首先要知道,要做成什么样,下面的HelloWorld类,就是要利用JavaPoet生成的目标。
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
接下来看看,怎么用JavaPoet写
//定义main方法
MethodSpec main = MethodSpec.methodBuilder("main")
//添加方法权限
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
//添加方法返回值
.returns(void.class)
//添加方法参数
.addParameter(String[].class, "args")
//添加方法体执行语句
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
//定义HelloWorld类
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
//生成Java文件
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
javaFile.writeTo(System.out);
上面注释已经很清楚,可以看出和书写习惯是相反来的,从下往上写,先生成方法,再生成类,最后生成包名,输出为文件。
改造之前APT写入文件的方式的方式
在上一篇中,是通过Writer生成文件
// 创建一个新的源文件(Class),并返回一个对象以允许写入它
val sourceFile = filer.createSourceFile("$packageName.$finalClassName")
// 定义Writer对象,开启写入
val writer: Writer = sourceFile.openWriter()
// 设置包名
writer.write("package $packageName;\n")
writer.write("public class $finalClassName {\n")
writer.write("public static Class<?> findTargetClass(String path) {\n")
// 获取类之上@ARouter注解的path值
val aRouter: ARouter = it.getAnnotation(ARouter::class.java)
writer.write(
"""
if (path.equals("${aRouter.path}")) {
""".trimIndent()
)
writer.write("return $className.class;\n}\n")
writer.write("return null;\n")
writer.write("}\n}")
这次改用JavaPoet
先看要生成的代码样子
package com.loveqrc.aptdemo;
public class MainActivityARouter {
public static Class<?> findTargetClass(String path) {
return path.equals("app/MainActivity")? MainActivity.class:null;
}
}
然后添加依赖
// 帮助通过类调用的形式来生成Java代码
implementation "com.squareup:javapoet:1.9.0"
// 高级写法,javapoet构建工具,参考(https://github.com/JakeWharton/butterknife)
try {
// 获取类之上@ARouter注解的path值
val aRouter: ARouter = it.getAnnotation(ARouter::class.java)
// 构建方法体
val method: MethodSpec = MethodSpec.methodBuilder("findTargetClass") // 方法名
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(Class::class.java) // 返回值Class<?>
.addParameter(String::class.java, "path") // 参数(String path)
// 方法内容拼接:
// return path.equals("/app/MainActivity") ? MainActivity.class : null
.addStatement(
"return path.equals($S) ? $T.class : null",
aRouter.path, ClassName.get(it as TypeElement?)
)
.build() // 构建
// 构建类
val type: TypeSpec = TypeSpec.classBuilder(finalClassName)
.addModifiers(Modifier.PUBLIC) //, Modifier.FINAL)
.addMethod(method) // 添加方法体
.build() // 构建
// 在指定的包名下,生成Java类文件
val javaFile: JavaFile = JavaFile.builder(packageName, type)
.build()
javaFile.writeTo(filer)
} catch (e: IOException) {
e.printStackTrace()
}
}
与Writer生成文件不同,javapoet是操作api来生成文件,从写的复杂度来说javapoet更加复杂些,无论哪种方法,最终目的是生成想要的java文件格式。