介绍
JavaPoet是用于生成.Java源文件的Java API。
当处理诸如注释处理或与元数据文件交互(例如,数据库模式、协议格式)时,源文件生成可能非常有用。通过生成代码,您消除了编写样板文件的需要,同时也保留了元数据的单一来源。
集成到项目中
<!-- https://mvnrepository.com/artifact/com.squareup/javapoet -->
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.11.0</version>
</dependency>
代码生成
类
//生成类
TypeSpec helloWorld=TypeSpec.classBuilder("HelloWorld")
.build();
//Java文件生成
JavaFile javaFile=JavaFile.builder("com.itcast.lyc",helloWorld).build();
try {
//把文件内容写入到 窗口打印出来
javaFile.writeTo(System.out);
} catch (IOException e) {
e.printStackTrace();
}
生成内容如下:
package com.itcast.lyc;
class HelloWorld {
}
方法
普通的方法列子
//生成类构造器
TypeSpec.Builder helloWorldBuilder=TypeSpec.classBuilder("HelloWorld");
MethodSpec main=MethodSpec.methodBuilder("main")//方法的构造器
.addParameter(String[].class,"args")//添加参数
.returns(void.class)//添加返回值
.addStatement("$T.out.println($S)",System.class,"helloWorld")//添加内容
.addStatement("$T.out.println($L)",System.class,100L)//显示字符
.build();
TypeSpec helloWorld=helloWorldBuilder.addMethod(main).build();
//Java文件生成
JavaFile javaFile=JavaFile.builder("com.itcast.lyc",helloWorld).build();
try {
//把文件内容写入到 窗口打印出来
javaFile.writeTo(System.out);
} catch (IOException e) {
e.printStackTrace();
}
其中$T代表类的.class类,使用$T和adddStatement匹配能自动导入包,$S表示注入字符串,adddStatement的一种注入字符串格式的语法,会自动帮语句添加分号和换行符,另外还有$L表示数字类型。
生成代码如下:
package com.itcast.lyc;
import java.lang.String;
import java.lang.System;
class HelloWorld {
void main(String[] args) {
System.out.println("helloWorld");
System.out.println(100);
}
}
方法添加代码的方式还有.addCode("asdasds;\n"),但是.addCode不会自动生成分号和换行,所以也不用,还可以这样写.addCode(CodeBlock.builder().addStatement("$S","|sdsad").build())。
控制流
JavaPoet为生成控制流,给我们提供了便利的API,比如,我们想生成如下内容:
if (true){
System.out.println("ok");
}
我们很难想像如果让我们自己打印{}括号带来的麻烦,所以JavaPoet在方法构造器中提供了beginControlFlow和endControlFlow。
MethodSpec main=MethodSpec.methodBuilder("main")//方法的构造器
.addParameter(String[].class,"args")//添加参数
.returns(void.class)//添加返回值
.addStatement("$T.out.println($S)",System.class,"helloWorld")//添加内容
.addStatement("$T.out.println($L)",System.class,100L)//显示字符
.beginControlFlow("if(true)")//控制流开始
.addStatement("$T.out.println($S)",System.class,"ok")
.endControlFlow()//控制流结束
.build();
抽象类
MethodSpec abstractMethon=MethodSpec.methodBuilder("testAbs")
.addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
.build();
TypeSpec typeSpec=TypeSpec.classBuilder("TestAbs")
.addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
.addMethod(abstractMethon)
.build();
接口类
MethodSpec abstractMethon=MethodSpec.methodBuilder("testInterfaces")
.addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
.build();
TypeSpec typeSpec=TypeSpec.interfaceBuilder("TestInterfaces")
.addModifiers(Modifier.PUBLIC)
.addField(FieldSpec.builder(String.class,"ONLY_ONCE")
.addModifiers(Modifier.PUBLIC,Modifier.STATIC,Modifier.FINAL)
.initializer("$S","OKKK")
.build())
//添加字段
.addMethod(abstractMethon)
.build();
构造方法
//构造方法
MethodSpec consructorMethod=MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(Integer.class,"age")//参数
.addStatement("this.$N=$N","age","age")//添加样式代码$N表示当前类的引用
.build();
TypeSpec helloWorld= helloWorldBuilder
.addMethod(main)//添加main方法
.addField(FieldSpec.builder(Integer.class,"age").addModifiers(Modifier.PRIVATE).build())//添加字段
.addMethod(consructorMethod)//添加构造方法
.build();
添加参数
.addParameter(Integer.class,"age")//参数
ParameterizedTypeName parameterizedTypeNameMap=ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(Integer.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(Object.class))
)
);
ParameterizedTypeName parameterizedTypeNameList=ParameterizedTypeName.get(
ClassName.get(List.class),
WildcardTypeName.subtypeOf(ClassName.get(Integer.class))
);
//添加带参数的方法
MethodSpec parmMethod=MethodSpec.methodBuilder("welomeBeiJing")
.addParameter(String.class,"parm")
.addParameter(ParameterSpec.builder(parameterizedTypeNameMap,"map").build())
.addParameter(ParameterSpec.builder(parameterizedTypeNameList,"list").build())
.build();
最后生成效果:
void welomeBeiJing(String parm, Map<Integer, Class<?>> map, List<? extends Integer> list) {
}
需要生成通配符的参数 使用
.addParameter(ParameterSpec.builder(TypeVariableName.get("T"),"t").build())
下面引用别人的图 加深理解:
添加字段
.addField(FieldSpec.builder(Integer.class,"age").addModifiers(Modifier.PRIVATE).build())//添加字段
Enums
File file=new File(System.getProperty("user.dir"),"\\src\\main\\java");
TypeSpec typeSpec=TypeSpec.enumBuilder("EnumsTest")
.addModifiers(Modifier.PUBLIC)
.addEnumConstant("Rock",TypeSpec.anonymousClassBuilder("$S","Page")
.addMethod(MethodSpec.methodBuilder("toString")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addStatement("return $S","avalanche")
.returns(String.class)
.build())
.build())
.addEnumConstant("Sci",TypeSpec.anonymousClassBuilder("$S","ssss").build())
.addEnumConstant("Apple",TypeSpec.anonymousClassBuilder("$S","sad").build())
.addField(String.class,"hand",Modifier.PRIVATE,Modifier.FINAL)
.addMethod(MethodSpec.constructorBuilder()
.addParameter(String.class,"hand")
.addStatement("this.$N=$N","hand","hand").build())
.build();
JavaFile javaFile=JavaFile.builder("com.itcast.lyc.javapoet",typeSpec).build();
javaFile.writeTo(file);
package com.itcast.lyc.javapoet;
import java.lang.Override;
import java.lang.String;
public enum EnumsTest {
Rock("Page") {
@Override
public String toString() {
return "avalanche";
}
},
Sci("ssss"),
Apple("sad");
private final String hand;
EnumsTest(String hand) {
this.hand=hand;
}
}
枚举类型通过TypeSpec.enumBuilder构造器进行添加枚举类。
内部类
TypeSpec comparator = TypeSpec.anonymousClassBuilder("")
.addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class))
.addMethod(MethodSpec.methodBuilder("compare")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(String.class, "a")
.addParameter(String.class, "b")
.returns(int.class)
.addStatement("return $N.length() - $N.length()", "a", "b")
.build())
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addMethod(MethodSpec.methodBuilder("sortByLength")
.addParameter(ParameterizedTypeName.get(List.class, String.class), "strings")
.addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator)
.build())
.build();
得到Java代码
void sortByLength(List<String> strings) {
Collections.sort(strings, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
}
内部类构造通过内部类构造方法TypeSpec.anonymousClassBuilder("")初始化内部类,然后使用$L引用对应内部类。
添加注释
字段、方法、类都可以添加注释
MethodSpec dismiss = MethodSpec.methodBuilder("dismiss")
.addJavadoc("Hides {@code message} from the caller's history. Other\n"
+ "participants in the conversation will continue to see the\n"
+ "message in their own history unless they also delete it.\n")
.addJavadoc("\n")
.addJavadoc("<p>Use {@link #delete($T)} to delete the entire\n"
+ "conversation for all participants.\n", Conversation.class)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addParameter(Message.class, "message")
.build();
效果
/**
* Hides {@code message} from the caller's history. Other
* participants in the conversation will continue to see the
* message in their own history unless they also delete it.
*
* <p>Use {@link #delete(Conversation)} to delete the entire
* conversation for all participants.
*/
void dismiss(Message message);
使用$T 引用类。
添加注解
使用AnnotationSpec.builder()添加注解:
MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addAnnotation(AnnotationSpec.builder(Headers.class)
.addMember("accept", "$S", "application/json; charset=utf-8")
.addMember("userAgent", "$S", "Square Cash")
.build())
.addParameter(LogRecord.class, "logRecord")
.returns(LogReceipt.class)
.build();
效果
@Headers(
accept = "application/json; charset=utf-8",
userAgent = "Square Cash"
)
LogReceipt recordEvent(LogRecord logRecord);