一、我们为什么要构建自己的代码生成器
通常来讲,在Java开发中最基本的也是占用我们最大量事件的就是CRUD,也就是频繁地构造“实体类”、“dao层代码”以及相关的service层和controller层的代码。其实,这一部分工作,可以使用代码生成器来完成,节约时间,免于加班。
二、一般代码生成器的底层原理
其实,包括mybatis代码生成器在内的所有代码生成器,其底层原理都是大差不差的,基本都是:
- 首先,写一个代码模板
- 然后,在模板中插入变量
- 最后,将变量替换为我们想要插入的值
经过这样的三步走,最终就是我们的想要生成的目标代码
当然,我们可以使用循环,判断等逻辑表达生成更加复杂,通用性更好的模板。
三、简单的例子——freemarker
下面,我们以freemarker为例,生成一个简单的代码模板。
准备工作
首先构建了一个自己的spring boot的maven工程
然后再pom里面引入了freemarker的依赖
<!-- 引入模板引擎freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
1. 写一个代码模板
我们下一个生成Java的模板最简单的模板(HelloWorld.java)
public class HelloWorld{
public void sayHelloToWorld(){
System.out.println("Hello, world");
}
}
2. 在模板中需要替换的地方中插入变量
这里就要用到,我们的freemarker引擎了,我们需要将刚刚的代码复制到Hello.ftl文件中,然后将World和world提出按成变量(注意这里区分大小写)
public class Hello${Domain}{
public void sayHelloTo${Domain}(){
System.out.println("Hello, ${domain}");
}
}
在上面我们用
D
o
m
a
i
n
替换了
W
o
r
l
d
,
{Domain}替换了World,
Domain替换了World,{domain}替换了world
不同的地方使用不同的变量
3. 将变量替换,生成我们自己的代码
实现代码替换生成文件,需要做三步:
第一步:读模板
读模板这里,freemarker有规范的写法,如下:
/**
* 读模板
*/
public static Template readTemplate(String ftlName, String ftlPath) throws IOException { // 这里的ftlName就是模板的路径
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setDirectoryForTemplateLoading(new File(ftlPath)); //
cfg.setObjectWrapper(new DefaultObjectWrapper(Configuration.VERSION_2_3_31));
Template template = cfg.getTemplate(ftlName);
return template;
}
第二步和第三步:将变量填进去,并输出出来
/**
* 根据模板,生成文件
*/
public static void generator(Template template, String fileName, Map<String, Object> map) throws IOException, TemplateException {
FileWriter fw = new FileWriter(fileName); // 输出的文件地址
BufferedWriter bw = new BufferedWriter(fw); // 构建一个输入流
template.process(map, bw); // 将map中的数据输入到template中
bw.flush(); // 关闭输入流
fw.close(); // 关闭文件
}
完整的代码应该是:
package com.liangyi.train.generator.test;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class ServerGeneratorTest {
public static void main(String[] args) {
String ftlName = "Hello.ftl";
String ftlPath = "generator/src/main/java/com/liangyi/train/generator/test"; // 这里的地址是从项目的根目录下开始算的,比如我这里项目根目录下有个文件夹generator
String fileName = "generator/src/main/java/com/liangyi/train/generator/test/HelloTeacher.java"; // 这里的地址是从项目的根目录下开始算的
HashMap<String, Object> map = new HashMap<>();
map.put("Domain", "Teacher");
map.put("domain", "teacher");
try{
Template template = readTemplate(ftlName, ftlPath);
generator(template, fileName, map);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
/**
* 读模板
* @param ftlName
* @param ftlPath
* @return
* @throws IOException
*/
public static Template readTemplate(String ftlName, String ftlPath) throws IOException { // 这里的ftlName就是模板的路径
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setDirectoryForTemplateLoading(new File(ftlPath)); // 设置模板文件的加载目录
cfg.setObjectWrapper(new DefaultObjectWrapper(Configuration.VERSION_2_3_31));
Template template = cfg.getTemplate(ftlName); // 从加载目录中加载文件名为ftlName指向的模板文件
return template;
}
/**
* 向文件中写入变量的值,并生长新文件
* @param template
* @param fileName
* @param map
* @throws IOException
* @throws TemplateException
* @throws TemplateException
*/
public static void generator(Template template, String fileName, Map<String, Object> map) throws IOException, TemplateException, TemplateException {
FileWriter fw = new FileWriter(fileName); // 输出的文件地址
BufferedWriter bw = new BufferedWriter(fw); // 构建一个输入流
template.process(map, bw); // 将map中的数据输入到template中
bw.flush(); // 关闭输入流
fw.close(); // 关闭文件
}
}