javapoet是一个开源项目,用于动态生成java源码。使用这个插件,你可以new出一个method,然后设置它的入参返回值,方法体等,最后输出这个方法的java源码。
原本考虑使用CodeModel这个插件,奈何这个插件过于古老且早已不在维护,以及更重要的一点,使用codemodel创建class实在过于繁琐,想想以后的工作量就让我窒息。
[infobox title="基础语法"][/infobox]
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);
如上是一个简单的Demo,构建了一个类和一个方法,其实这代码是构建AST的过程,这些使用到的类都包含了emit方法,含义和toString一致,用于输出当前元素的java源码,通过递归调用生成整个源码,使用起来和JSqlParse差不多,不停的new一个个的元素,然后配置组装各种元素。只不过jSqlParse还支持代码解析到AST实现动态修改。
而javapoet没办法把java源码转换成对象,也就没办法修改现成的代码。
控制流 最终生成{}代码块
beginControlFlow
nextControlFlow
endControlFlow
迭代循环体 $L 最终的操作和string.format类似
beginControlFlow("for (int i = $L; i < $L; i++)", from, to)
$S 表示字符串变量
addStatement("return $S", name)
$T 表示类型 会生成对应的import
addStatement("return new $T()", Date.class)
ClassName 构建一个编译环境不存在的类
ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
ParameterizedTypeName 构建泛型 list
ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
ClassName list = ClassName.get("java.util", "List");
TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard);
静态导入
addStaticImport(namedBoards, "*")
$N 调用类中已定义的方法
.addStatement("result[1] = $N(b & 0xf)", hexDigit)
CodeBlock 代码块格式化
// zfc占位符 增强版 $indexL index > 0
CodeBlock.builder().add("I ate $2L $1L", "tacos", 3).build().toString()
// 直接读取属性
Map<String, Object> map = new LinkedHashMap<>(); map.put("food", "tacos"); map.put("count", 3); CodeBlock.builder().addNamed("I ate $count:L $food:L", map).build().toString()
定义抽象方法和抽象类
MethodSpec flux = MethodSpec.methodBuilder("flux") .addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addMethod(flux) .build();
构造函数
MethodSpec flux = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(String.class, "greeting") .addStatement("this.$N = $N", "greeting", "greeting") .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addField(String.class, "greeting", Modifier.PRIVATE, Modifier.FINAL) .addMethod(flux) .build();
方法参数
ParameterSpec android = ParameterSpec.builder(String.class, "android") .addModifiers(Modifier.FINAL) .build(); MethodSpec welcomeOverlords = MethodSpec.methodBuilder("welcomeOverlords") .addParameter(android) .addParameter(String.class, "robot", Modifier.FINAL) .build();
变创建量
FieldSpec android = FieldSpec.builder(String.class, "android") .addModifiers(Modifier.PRIVATE, Modifier.FINAL) .initializer("$S + $L", "Lollipop v.", 5.0d) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addField(android) .addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL) .build();
定义接口 方法和属性的修饰符固定不能变 但输出的时候 又会忽略
TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addField(FieldSpec.builder(String.class, "ONLY_THING_THAT_IS_CONSTANT") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", "change") .build()) .addMethod(MethodSpec.methodBuilder("beep") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .build()) .build();
定于一个枚举类
TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") .addModifiers(Modifier.PUBLIC) .addEnumConstant("ROCK") .addEnumConstant("SCISSORS") .addEnumConstant("PAPER") .build();
一个比较复杂的枚举
TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") .addModifiers(Modifier.PUBLIC) .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("$S", "fist") .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .addStatement("return $S", "avalanche!") .returns(String.class) .build()) .build()) .addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace") .build()) .addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat") .build()) .addField(String.class, "handsign", Modifier.PRIVATE, Modifier.FINAL) .addMethod(MethodSpec.constructorBuilder() .addParameter(String.class, "handsign") .addStatement("this.$N = $N", "handsign", "handsign") .build()) .build();
匿名内部类
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();
添加注解
.addAnnotation(Override.class) .addAnnotation(AnnotationSpec.builder(Headers.class) .addMember("accept", "$S", "application/json; charset=utf-8") .addMember("userAgent", "$S", "Square Cash") .build())
注解组合
MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(HeaderList.class) .addMember("value", "$L", AnnotationSpec.builder(Header.class) .addMember("name", "$S", "Accept") .addMember("value", "$S", "application/json; charset=utf-8") .build()) .addMember("value", "$L", AnnotationSpec.builder(Header.class) .addMember("name", "$S", "User-Agent") .addMember("value", "$S", "Square Cash") .build()) .build()) .addParameter(LogRecord.class, "logRecord") .returns(LogReceipt.class) .build(); @HeaderList({ @Header(name = "Accept", value = "application/json; charset=utf-8"), @Header(name = "User-Agent", value = "Square Cash") }) LogReceipt recordEvent(LogRecord logRecord);