自定义注解生成代码(二) 之javapoetAPI使用举例
上一篇我们详细的介绍了javapoet的API,为了更详细的使用这里追加一篇简单使用的介绍
感兴趣的朋友可以查看
JavaPoet
JavaPoet 是一个用来生成 .java源文件的Java API。
当做如注解或者数据库模式、协议格式等事情时,生成源文件就比较有用处。
Example
以 HelloWorld 类为例:
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
上面的代码就是使用javapoet用下面的代码进行生成的:
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();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
javaFile.writeTo(System.out);
通过MethodSpec类来创建一个”main”方法,并配置了修饰符、返回值类型、参数以及代码语句。然后把这个main方法添加到 HelloWorld 类中,最后添加到 HelloWorld.java文件中。
这个例子中,我们将文件通过Sytem.out 进行输出,但是同样也可以使用(JavaFile.toString()) 得到string字符串,或者通过 (JavaPoet.writeTo()) 方法写入到文件系统中。
Javadoc 中包括了完整的JavaPoet API, 我们接着往下看。
Code & Control Flow
大多数JavaPoet的API使用的是简单的不可变的Java对象。通过建造者模式,链式方法,可变参数是的API比较友好。JavaPoet提供了(TypeSpec)用于创建类或者接口,(FieldSpec)用来创建字段,(MethodSpec)用来创建方法和构造函数,(ParameterSpec)用来创建参数,(AnnotationSpec)用于创建注解。
但是如果没有语句类,没有语法结点数,可以通过字符串来构建代码块:
MethodSpec main = MethodSpec.methodBuilder("main")
.addCode(""
+ "int total = 0;\n"
+ "for (int i = 0; i < 10; i++) {\n"
+ " total += i;\n"
+ "}\n")
.build();
生成的代码如下:
void main() {
int total = 0;
for (int i = 0; i < 10; i++) {
total += i;
}
}
人为的输入分号、换行和缩进是比较乏味的。所以JavaPoet提供了相关API使它变的容易。
addStatement() 负责分号和换行,beginControlFlow() + endControlFlow() 需要一起使用,提供换行符和缩进。
MethodSpec main = MethodSpec.methodBuilder("main")
.addStatement("int total = 0")
.beginControlFlow("for (int i = 0; i < 10; i++)")
.addStatement("total += i")
.endControlFlow()
.build();
这个例子稍微有点差劲。生成的代码如下:
private MethodSpec computeRange(String name, int from, int to, String op) {
return MethodSpec.methodBuilder(name)
.returns(int.class)
.addStatement("int result = 0")
.beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)")
.addStatement("result = result " + op + " i")
.endControlFlow()
.addStatement("return result")
.build();
}
调用computeRange(“multiply10to20”, 10, 20, “*”)就生成如下代码:
int multiply10to20() {
int result = 0;
for (int i = 10; i < 20; i++) {
result = result * i;
}
return result;
}
方法生成方法!JavaPoet生成的是源代码而不是字节码,所以可以通过阅读源码确保正确。
$L for Literals
字符串连接的方法beginControlFlow() 和 addStatement是分散开的,操作较多。
针对这个问题, JavaPoet 提供了一个语法但是有违String.format()语法. 通过 $L 来接受一个 literal 值。 这有点像 Formatter’s %s:
private MethodSpec computeRange(String name, int from, int to, String op) {
return MethodSpec.methodBuilder(name)
.returns(int.class)
.addStatement("int result = 0")
.beginControlFlow("for (int i = $L; i < $L; i++)", from, to)
.addStatement("result = result $L i", op)
.endControlFlow()
.addStatement("return result")
.build();
}
Literals 直接写在输