介绍
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);