系列文章:
一 前言
上一篇文章简单介绍了工程的初始化方法,本篇将探索代码生成技术。因为业务开发中使用Java语言较多,所以这里以Java作为背景语言。
大家熟知并且日常都在使用Spring/SpringMVC/SpringBoot、Mybatis框架,在开发中我们经常需要手写Entity、Mapper、dao、service、controller相关代码,这些重复性的工作会占用很多工作时间。如果有一个代码生成工具来做这些重复工作,显然可以提高我们的工作效率。这时,就需要了解模板引擎技术。
二 模板引擎工具:freemarker与velocity
最早接触的是velocity,记得14,15年左右,当时在某家公司开发的前端页面,就是使用velocity作为模板引擎。创建前端页面的vm/tpl模板后,利用velocity提供的能力,编写脚本生成静态页面,然后再走上线更新。
除了velocity之外,freemarker也是一款模板引擎,使用FreeMarker Template Language(FTL)编写,它是一种简单的、专用的语言。
关于二者(有时也会包括thymeleaf)的对比,已经有一些文章进行了分析,这里不再赘述,感兴趣的朋友可以搜索,或在评论中留言一起探讨。这里基于以前的一些调研工作,选择freemarker作为这里使用的模板引擎,用于示例。
三 freemarker介绍
freemarker的基础介绍,可以参考freemarker在线手册,中文版地址:http://freemarker.foofun.cn/。
简单来说,FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
freemarker的几个关键概念:模板(template)、Java对象(Java Object)、输出(Output),三者含义和关系可通过下图体现:
(1)其中,template是我们要使用的模板,基于这个模板来生成文件。简单来说,就是一些固定模式(代码/标签/逻辑)+变量的组合,其中变量在后面根据需要,替换成所需的值;
(2)Java Object是模型/对象,可以简单理解为一些key-value对,key是变量名称,value就是变量的值;
(3)Output是输出结果,在把template中的变量替换成object中的value后,就得到了我们预期的文件。
而替换的动作,就是由freemarker来实现的。
四 示例
4.1 工程结构
这里采用maven工程,idea创建,工程目录结构如下:
4.2 依赖包引入-pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>template-engine</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> <finalName>${project.artifactId}</finalName> </build> </project>
4.3 自定义freemark模板
这里预期是生成一个Java类,如下所示,是一个典型的bean,包括几个属性和对应的get/set方法,几个变量: packageName-包名,className-类名,属性:id, userName, password
package ${packageName}; public class ${className} { private Integer ${id}; private String ${userName}; private String ${password}; public Integer get${id?cap_first}(){ return ${id}; } public void set${id?cap_first}(Integer ${id}){ this.${id}=${id}; } public String get${userName?cap_first}(){ return ${userName}; } public void set${userName?cap_first}(String ${userName}){ this.${userName}=${userName}; } public String get${password?cap_first}(){ return ${password}; } public void set${password?cap_first}(String ${password}){ this.${password}=${password}; } }
4.4 定义生成代码方法
package com.freemark.demo.templates.util; import freemarker.template.Configuration; import freemarker.template.Template; import java.io.*; import java.util.HashMap; import java.util.Map; public class FreemarkTest { private static final String TEMPLATE_PATH = "src/main/java/com/freemark/demo/templates"; private static final String CLASS_PATH = "src/main/java/com/freemark/demo"; public static void main(String[] args) { // 创建freeMarker配置实例 Configuration configuration = new Configuration(); Writer out = null; try { // 设置模版路径 configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH)); // 创建数据模型 Map<String, Object> dataMap = new HashMap<>(); dataMap.put("packageName", "com.freemark.demo"); dataMap.put("className", "Test"); dataMap.put("id", "id"); dataMap.put("userName", "userName"); dataMap.put("password","password"); // 加载模版文件 Template template = configuration.getTemplate("test.ftl"); // 生成文件流 File docFile = new File(CLASS_PATH + "/" + "Test.java"); out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile))); // 输出到文件 template.process(dataMap, out); System.out.println("Test.java 源码生成成功!"); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != out) { out.flush(); } } catch (Exception e2) { e2.printStackTrace(); } } } }
我们运行main方法,可以看到控制台会打印出 Test.java 源码生成成功! 并在src/main 指定的包下,生成了Test.java类文件,内容如下:
package com.freemark.demo; public class Test { private Integer id; private String userName; private String password; public Integer getId(){ return id; } public void setId(Integer id){ this.id=id; } public String getUserName(){ return userName; } public void setUserName(String userName){ this.userName=userName; } public String getPassword(){ return password; } public void setPassword(String password){ this.password=password; } }