Java开源工具库使用之java源代码生成库JavaPoet
文章目录
- 前言
- 一、API
- 1.1 字段
- 1.2 方法
- 1.3 代码块
- 1.4 类
- 1.5 java 文件
- 二、使用例子
- 2.1 数据库表生成 Bean
- 2.2 Service测试类生成
- 参考
前言
JavaPoet 是一个用于生成 .java 源代码文件的 Java API。截止博客发表为止,整个项目核心源码就17个类文件,github上却有 9.9k stars,可谓短小精悍。
javapoet 实现了自动导包和语句分号添加,代码流程控制,代码格式化等功能,很方便开发者生成一些模板代码
github 地址: https://github.com/square/javapoet
pom 依赖:
<dependency><groupId>com.squareup</groupId><artifactId>javapoet</artifactId><version>1.13.0</version>
</dependency>
一、API
我们知道,一个 java 类源码文件由类、字段、构造方法、方法、参数、注解等元素组成,JavaPoet 为这些基本组成元素分别定义了相应的类,分别用来管理、生成相应元素相关的代码。
JavaPoet 所有类:
class | 说明 |
---|---|
AnnotationSpec | 用于生成注解 |
ArrayTypeName | 定义数组类型需要用这个类 |
ClassName | 对应一个类、接口或 enum 的名字,由 package 名字和类名字两部分组成 |
CodeBlock | 用于生成代码块 |
CodeWriter | 代码生成类,将 JavaFile 转换为人类可读和javac使用的字符串。 |
FieldSpec | 用于生成字段 |
JavaFile | 用于生成.java文件 |
LineWrapper | 行相关 |
MethodSpec | 用于生成方法或构造方法 |
NameAllocator | 指定Java标识符名称以避免冲突、关键字和无效字符 |
ParameterizedTypeName | 泛型中的参数化类型 |
ParameterSpec | 用于生成参数 |
TypeName | 类名字 |
TypeSpec | 类、接口或Enum |
TypeVariableName | 泛型中的类型变量 |
Util | 工具类 |
WildcardTypeName | 泛型中的通配符? |
JavaPoet支持语句中变量替换,几个常用替换符号的功能介绍如下:
替换符号 | 说明 |
---|---|
$T | 参数是Class对象,替换为Class的名字,如果需要,同时在文件头添加相应的 import 语句 |
$L | 替换为变量值的字面量值,功能相当于字符串的format()方法 |
$S | 也是替换为变量值的字面量值,但是字面量值为被字符串双引号包裹起来 |
$N | 参数是ParameterSpec、TypeSpec、MethodSpec等,替换为这些变量的name值 |
变量、参数、返回值的类型即可以用 java 的 class 来表达,javapoet 也定义了相应的类型表示系统,对应关系如下:
类别 | 生成的类型举例 | javapoet 表达方式 |
---|---|---|
基本类型 | int | TypeName.INT |
基本类型包装类型 | Integer | Integer.class |
数组 | int[] | ArrayTypeName.of(int.class) |
对象类型 | String | String.class |
参数化类型 | List<String> | ParameterizedTypeName.get(List.class, String.class) |
类型变量 | T | TypeVariableName.get(“T”) |
通配符类型 | ? extends String | WildcardTypeName.subtypeOf(String.class) |
1.1 字段
使用 FieldSpec 可以设置字段类型和名字,还可以设置初始值,和修饰符 public/private/protect
注:javapoet 中 Modifier修饰符都是 javax.lang.model.element.Modifier,而不是 java.lang.reflect.Modifier
// String 类型
FieldSpec name = FieldSpec.builder(String.class, "name").addModifiers(Modifier.PUBLIC).build();
// int 类型
FieldSpec age = FieldSpec.builder(TypeName.INT, "age").addModifiers(Modifier.PRIVATE).initializer("12").build();
// String 数组类型
FieldSpec skills = FieldSpec.builder(ArrayTypeName.of(String.class), "skills").addModifiers(Modifier.PUBLIC).build();
能够生成下面代码
public String name;private int age = 12;public String[] skills;
1.2 方法
方法使用 MethodSpec 来构建方法,常用的可以添加修饰符public,返回值,参数,语句等等
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();
生成 java 代码 如下
public static void main(String[] args) {System.out.println("Hello, JavaPoet!");
}
除了以上,还支持添加注解, 代码块,异常,javadoc,注释等等
CodeBlock codeBlock = CodeBlock.builder().addStatement("int i = 0").addStatement("$T.out.println(i)", System.class).build();MethodSpec main = MethodSpec.methodBuilder("main").addModifiers(Modifier.PUBLIC, Modifier.STATIC).returns(void.class).addParameter(String[].class, "args").addAnnotation(Benchmark.class).addException(Exception.class).addJavadoc("this is javapoet demo").addCode(codeBlock).addComment("这是注释").addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!").addComment("这是另一段注释").build();
生成方法
/*** this is javapoet demo*/
@Benchmark
public static void main(String[] args) throws Exception {int i = 0;System.out.println(i);// 这是注释System.out.println("Hello, JavaPoet!");// 这是另一段注释
}
此外,还支持构造方法等等
MethodSpec m = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).build();
1.3 代码块
代码块使用 CodeBlock 来生成,可以使用 beginControlFlow endControlFlow 来控制流程,特别是减少了循环和 if 的大括号手动添加
CodeBlock codeBlock = CodeBlock.builder().addStatement("int i = 0").beginControlFlow("for (;i < 10; i++)").addStatement("$T.out.println(i)", System.class).endControlFlow().beginControlFlow("if (i > 9)").beginControlFlow("if (i < 15)").addStatement("i = 200").nextControlFlow("else ").addStatement("i = 300").endControlFlow().addStatement("i++").endControlFlow().build();
生成代码
int i = 0;for (;i < 10; i++) {System.out.println(i);}if (i > 9) {if (i < 15) {i = 200;} else {i = 300;}i++;}
异常代码块也可以用 nextControlFlow 控制
CodeBlock cb = CodeBlock.builder().beginControlFlow("try").addStatement("throw new Exception($S)", "Failed").nextControlFlow("catch ($T e)", Exception.class).addStatement("throw new $T(e)", RuntimeException.class).endControlFlow().build();
生成代码
try {throw new Exception("Failed");} catch (Exception e) {throw new RuntimeException(e);}
1.4 类
类使用 TypeSpec 来生成,可以添加字段,方法,构造方法,静态代码块,接口,父类等等方法
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld").addModifiers(Modifier.PUBLIC, Modifier.FINAL).addField(name).addField(age).addField(skills).addAnnotation(Benchmark.class).addSuperinterface(Serializable.class).superclass(AbstractList.class).addMethod(main).addMethod(m).build();
不仅可以生成普通类,还可以生成枚举,接口,匿名类等
TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo").addModifiers(Modifier.PUBLIC).addEnumConstant("ROCK").addEnumConstant("SCISSORS").addEnumConstant("PAPER").build();
生成代码如下
public enum Roshambo {ROCK,SCISSORS,PAPER
}
1.5 java 文件
最终 java 文件可以通过 JavaFile 生成
JavaFile javaFile = JavaFile.builder("com.demo.helloworld", helloWorld).skipJavaLangImports(true).build();
// 在控制台打印代码
javaFile.writeTo(System.out);
// 将代码存储为文件
javaFile.writeTo(new File("D:\\java"));
二、使用例子
2.1 数据库表生成 Bean
将一个数据库的所有表转化为实体类。使用 spring-boot-starter-jdbc 连接数据库,guava库实现表和字段的驼峰转化,实体类带有lombok的@Data注解
@SpringBootTest
public class EnityGenerate {@Autowiredprivate JdbcTemplate jdbcTemplate;@Testpublic void main() {String databaseName = "test";String packageName = "com.aabond.demo.enity";List<Map<String, Object>> list = jdbcTemplate.queryForList("select\n" +"\t\ttable_name,\n" +" column_name,\n" +" data_type\n" +"from information_schema.columns\n" +"where table_schema = '" + databaseName + "'");Map<Object, List<Map<String, Object>>> name = list.stream().collect(Collectors.groupingBy(m -> m.get("table_name")));name.forEach((tableName, fields) -> {Map<String, Type> tmp = new HashMap<>();for (Map<String, Object> field : fields) {String columnName = (String) field.get("column_name");String dataType = (String) field.get("data_type");try {tmp.put(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, columnName), mysqlTypeToJavaType(dataType));} catch (Exception e) {throw new RuntimeException(e);}}String className = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, (String) tableName);TypeSpec generate = generate(className, tmp);JavaFile javaFile = JavaFile.builder(packageName, generate).indent(" ").skipJavaLangImports(true).build();try {javaFile.writeTo(Paths.get("src/main/java"));} catch (IOException e) {throw new RuntimeException(e);}});}private Type mysqlTypeToJavaType(String mysqlType) throws Exception {switch (mysqlType) {case "varchar":case "blob":case "text":case "tinyblob":return String.class;case "int":return Integer.class;case "bigint":return Long.class;case "timestamp":return LocalDateTime.class;case "decimal":return BigDecimal.class;default:throw new Exception("unknow mysql type: " + mysqlType);}}private TypeSpec generate(String table, Map<String, Type> field) {List<FieldSpec> collect = field.entrySet().stream().map((a) -> FieldSpec.builder(a.getValue(), a.getKey(), Modifier.PRIVATE).build()).collect(Collectors.toList());TypeSpec typeSpec = TypeSpec.classBuilder(table).addFields(collect).addAnnotation(Data.class).build();return typeSpec;}
}
2.2 Service测试类生成
将service层的所有接口的所有方法生成测试类
@Test
public void serviceTestGenerate() {String packagePath = "src/main/java/com/aabond/demo/service";String packageName = "com.aabond.demo.service";String testPath = "src/test/java";File path = Paths.get(packagePath).toFile();File[] files = path.listFiles(file -> file.getName().endsWith("Service.java"));System.out.println(Arrays.toString(files));Arrays.stream(Objects.requireNonNull(files)).map(f -> {try {return Class.forName(packageName + "." + f.getName().replace(".java", ""));} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}).forEach(c -> {String name = c.getSimpleName();Method[] methods = c.getMethods();List<String> strings = Arrays.stream(methods).map(Method::getName).collect(Collectors.toList());TypeSpec generate = generate(name, strings);JavaFile javaFile = JavaFile.builder(packageName, generate).indent(" ").skipJavaLangImports(true).build();try {// javaFile.writeTo(System.out);javaFile.writeTo(Paths.get(testPath));} catch (IOException e) {throw new RuntimeException(e);}});}private TypeSpec generate(String className, List<String> methodNames) {List<MethodSpec> collect = methodNames.stream().map((a) -> MethodSpec.methodBuilder(a).addModifiers(Modifier.PUBLIC).addAnnotation(Test.class).build()).collect(Collectors.toList());TypeSpec typeSpec = TypeSpec.classBuilder(className).addMethods(collect).build();return typeSpec;
}
参考
- 基于JavaPoet自动生成java代码文件
Java开源工具库使用之java源代码生成库JavaPoet相关推荐
- 这些个JAVA开源工具(那是相当地多啊)
OpenCms [Java开源 CMS系统] OpenCms是一个J2EE的产品,它是用Java写成的.它和Tomcat捆绑在一起.但是也能够使用ATG Dynamo.WebLogic和WebSphe ...
- java开源工具简介
来源:http://baike.baidu.com/view/629119.htm 通俗点说, 就是你写了一个软件, 然后把这个软件的源代码发布到网上, 让大家都可以学习,改进. 就是开源! 专业点说 ...
- java开源工具简介(2)
java开源工具简介(2) OpenCms [Java开源 CMS系统] OpenCms是一个J2EE的产品,它是用Java写成的.它和Tomcat捆绑在一起.但是也能够使用ATG Dynamo.We ...
- Java开源生鲜电商平台-Java分布式以及负载均衡架构与设计详解(源码可下载)
Java开源生鲜电商平台-Java分布式以及负载均衡架构与设计详解(源码可下载) 说明:主要是针对一些中大型的项目需要进行分布式以及负载均衡的架构提一些思路与建议. 面对大量用户访问.高并发请求,海量 ...
- Java开源工具库使用之Apache commons-lang3
文章目录 前言 一.字符串 1.1 StringUtils 1.2 CharSetUtils 1.3 RegExUtils 1.4 RandomStringUtils 二.数字 2.1 NumberU ...
- JAVA开源工具大全
文章来源:open-open Spring Framework [Java开源 J2EE框架] Spring是一个解决了许多在J2EE开发中常见的问题的强大框架. Spring提供了管理业务对象的一 ...
- iOS应用开发的五个Java开源工具
随着第三方工具的不断壮大,开发人员逐渐摆脱政策束缚,对于iOS系统的封闭性为其他语言(如Java)开发者诟病得到解脱,开始使用自己熟悉的语言来编写iOS本地应用,或将其他平台上的应用移植到iOS上. ...
- java 开源商城_让这个Java语言的开源商城系统火起来
Java是一门非常优秀的面向对象编程语言,功能强大且简单易用,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,凭借其简单性.面向对象.分布式.健壮性.安全性.平台独立与可 ...
- java开源博客系统solo_Solo Java 开源博客
Solo 是一款一个命令就能搭建好的 Java 开源博客系统,如果你想开个独立博客,请一定不要错过!2.5.0 版本主要支持了 Markdown/JSON 格式数据导出,并改进了 Markdown 渲 ...
最新文章
- [你必须知道的.NET]第二十一回:认识全面的null
- 【剑指offer-Java版】44扑克牌的顺子
- linux 判断网卡是否异常_如何判断linux网卡故障?
- python3.7.1使用_在不影响使用python3.7.1的功能的情况下,是否可以从python代码中删除所有的ufuture_uu语句?...
- 【ES6(2015)】Number
- C#算法设计排序篇之09-基数排序(附带动画演示程序)
- ip别名及其在tcp压力测试时候的作用
- 打印表单_超市生鲜日常作业表单,打印出来就能用!
- 一个果农儿子的心声,你倾听一下吧?
- 2022年电工杯B题参赛历程
- 谷歌翻译API-python接口-Googletrans
- 基于单片机的功放protues_基于单片机的功放毕业设计
- 如何删除Word 2010中的“向下箭头”
- 【椭球大地测量学】Python及MATLAB实现大地坐标与空间直角坐标间的转换编程(含流程图)
- mysql实现oracle的同义词_ORACLE同义词总结(上)
- [Spark调优]--TaskSetManager的有效Locality Levels
- 阿里巴巴入选的JCP最高执行委员会,何方神圣?
- OpenGLES2.0渲图步骤:绘几何图形、图片处理、离屏渲染(3)
- 再获喜讯,思迈特软件入选2021企业数智化转型升级服务全景图
- SuperMap iDesktopX安装 ---(保密机:龙芯CPU+银河麒麟系统)
热门文章
- redux与flux
- oracle01109,oracle解锁用户时报错ORA-01109: database not open的解决办法
- @Validated和@Valid使用
- 电商搜索“优化商品排序”实践方案
- Photoshop切图简单设置+工具介绍+以及切图方法
- python批量添加qq好友_python实现QQ批量登录功能
- cisco设备接口下配置encapsulation dot1q vlan-id的作用
- 遇到问题--python-- pandas--常见问题积累
- hw1-浅谈Dota2设计元素
- 大数的四则运算(加,减,乘,除)处理