手把手教你开发一套代码生成器,学不会的来怼我!

?一、介绍在实际的软件项目开发过程中,我可以很负责任的跟大家说,如果你真的实际写代码的时间超过5年,你对增删改查这类简单的功能需求开发,可以说已经完全写吐了,至少我就是这种类型的 。
但是呢,不可否认,绝大多数的软件功能,向下追随到最基本的单元,也基本都是单表的增、删、改、查!
只是随着用户需求不断增多,原来可能一个张单表就可以搞定的事情,现在可能需要多张表,或者多个库才能搞定,代码层就像堆积木一样,越堆越复杂 。
我记得早期做项目的时候,项目每新加一张单表,我都需要在代码层,按照MVC?框架的思想,重新编写一套CURD的代码,写完所有的基础的增删改查,至少需要20分钟,手快的情况下,最快也要10分钟 。
假如某个新开发的功能,要新增10张表,按照这个时间计算,至少要100分钟,仔细想想,其实你会发现大部分的时间都浪费在这些简单而又重复的编程圈子中去了 。
那有没有一个办法,将这些简单的CURD代码,全部都标准化、公共化呢?这样我们的可以省下很多时间来投入业务场景的开发 。
答案是肯定的,有!
我记得早期我最先接触的是MyBatisGenertor?工具包,通过这个工具包,我们可以省去大部分的mybaits中xml?文件的curd编写工作 。
还有我们所熟悉的JPA?,里面有一套公共的持久层动态代理类,它可以自动根据名称生成SQL语句,能为开发省下不少的事情 。
但是我这个人比较懒,我想搞一个工具,从controller、service、entity 、dao?层,全部的crud代码,包括单元测试类,通过工具自动生成好 。
像这样的工具,现在网上也有不少,例如我们所熟悉的Mybatis-plus插件,它就可以做到这一点,也是非常好用 。
但是有的公司就不喜欢它,原因也很简单,里面的很多公共方法封装的过于深入,而且很多crud的sql全部都是动态生成,你根本看不到 。
总之啊就是一句,不在自己掌控之内的,很多程序员总是带着各种疑虑~~
当然,还有一个明显的疑虑,就是对微服务的开发,不能全面支持,比如你项目采用的是SpringBoot +Dubbo?组合来开发,这个时候生成的controller,完全没啥用处,而且还很鸡肋 。
因此在这种情况下,你得基于当前的项目软件开发规则,自己开发一套代码生成器,以满足快速开发的需要 。
下面我就简单的介绍一下,如何自行开发一套代码生成器,过程如下!
二、代码实践其实开发一套代码生成器,真没大家想象中的那么复杂,其中用的最重要一项技术,就是利用模板来生成代码,例如我们经常使用的模板引擎freemarker,它就可以帮助我们实现这一点 。
2.1、首先我们添加 freemarker 依赖包<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.23</version></dependency>2.2、然后创建一个代码模版下面我们以动态创建实体类为例,编写一个实体类的模板entity.JAVA.ftl?,其中${}里面定义的是动态变量 。
package ${package};import java.io.Serializable;/** * <p> * ${tableComment} * </p> * * @author ${author} * @since ${date} */public class ${entityClass} implements Serializable {private static final long serialVersionUID = 1L;<#--属性遍历--><#list columns as pro>/*** ${pro.comment}*/private ${pro.propertyType} ${pro.propertyName};</#list><#--属性get||set方法--><#list columns as pro>public ${pro.propertyType} get${pro.propertyName?cap_first}() {return this.${pro.propertyName};}public ${entityClass} set${pro.propertyName?cap_first}(${pro.propertyType} ${pro.propertyName}) {this.${pro.propertyName} = ${pro.propertyName};return this;}</#list>}2.3、最后生成目标代码最后我们基于freemarker编写一个测试类!
public class CodeGeneratorDemo {public static void main(String[] args) throws IOException, TemplateException {Map<String, Object> objectMap = new HashMap<>();//定义包路径objectMap.put("package", "com.example.test");//定义实体类objectMap.put("entityClass", "Student");//定义实体类属性List<Map<String, Object>> columns = new ArrayList<>();//姓名字段Map<String, Object> column1 = new HashMap<>();column1.put("propertyType", "String");column1.put("propertyName", "name");column1.put("comment", "姓名");columns.add(column1);//年龄字段Map<String, Object> column2 = new HashMap<>();column2.put("propertyType", "Integer");column2.put("propertyName", "age");column2.put("comment", "年龄");columns.add(column2);//定义类的属性objectMap.put("columns", columns);//定义作者objectMap.put("author", "张三");//定义创建时间objectMap.put("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));//定义类描述objectMap.put("tableComment", "学生信息");//生产目标代码Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);configuration.setDefaultEncoding(Charset.forName("UTF-8").name());configuration.setClassForTemplateLoading(CodeGeneratorDemo.class, "/");Template template = configuration.getTemplate("/templates/entity.java.ftl");FileOutputStream fileOutputStream = new FileOutputStream(new File("../src/main/java/com/example/generator/Student.java"));template.process(objectMap, new OutputStreamWriter(fileOutputStream, Charset.forName("UTF-8").name()));fileOutputStream.close();System.out.println("文件创建成功");}}


推荐阅读