对于这个问题 最简洁直接的回答就是:代替手动编写代码、提高工作效率。
什么样的场景和代码适合用自动生成这种方式呢?
做过Java服务端的朋友一定都知道代码中我们需要编写与数据库表映射的Java实体类(Entity)、需要编写与实体对应的DAO类(XxDao.java类中有包含对应实体的增、删、改、查基本操作)。在这些实体类中通常都是一些属性方法以及属性对应的get/set方法、而实体对应的DAO类中也基本会包含有增、删、改、查这些与数据库操作相关的方法。在编写了那么多的实体类和Dao类的过程中 你是否发现了这些代码中有很多地方都相似或者差不多、只是名字不同而已呢?对、那么这个时候其实我们可以定义一个模板、通过模板我们来让代码自动生成去吧。
二、FreeMarker的简单介绍
在进入正文前,让我们首先简单、快速了解一下FreeMarker。
(做过Web开发的朋友肯定都是相当熟悉的、小吕当时 也是在做Web开发的时候第一次接触了FreeMarker)
1、概述:FreeMarker是一款模板引擎:即一种基于模板、用来生成输出文本的通用工具。更多的是被用来设计生成HTML页面。
简单说就是:FreeMarker是使用模板生成文本页面来呈现已经准备好的数据。如下图表述
FreeMarker官网:http://freemarker.org/
2、通过一个简单的例子来展示如何使用FreeMarker定义模板、绑定模型数据、生成最终显示的Html页面:
1>.新建项目 在项目根目录下新建template文件夹,用来存放我们的Template file,
如我们新建模板文件test.ftl 内容如下:
1
|
|
Welcome ${user}<#if user == Big Joe>, our beloved leader!
Our latest product: ${latestProduct.name}!
2>.项目引入freemarker.jar(下载地址:https://jarfiles.pandaidea.com/freemarker.html),
在Java类中使用FreeMarker API方法引用模板文件、创建数据模型、合并数据模型与模板文件最终输入,
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
import
java.io.File;
import
java.io.IOException;
import
java.io.OutputStreamWriter;
import
java.io.Writer;
import
java.util.HashMap;
import
java.util.Map;
import
freemarker.template.Configuration;
import
freemarker.template.DefaultObjectWrapper;
import
freemarker.template.Template;
import
freemarker.template.TemplateException;
public
class
HtmlGeneratorClient {
public
static
void
main(String[] args) {
try
{
Configuration cfg =
new
Configuration();
// 指定模板文件从何处加载的数据源,这里设置成一个文件目录
cfg.setDirectoryForTemplateLoading(
new
File(./template));
cfg.setObjectWrapper(
new
DefaultObjectWrapper());
// 获取或创建模板
Template template = cfg.getTemplate(test.ftl);
// 创建数据模型
Map root =
new
HashMap();
root.put(user, Big Joe);
Map latest =
new
HashMap();
root.put(latestProduct, latest);
latest.put(url, http:
//blog.csdn.net/janice0529/article/details/products/greenmouse.html);
latest.put(name, green mouse);
// 将模板和数据模型合并 输出到Console
Writer out =
new
OutputStreamWriter(System.out);
template.process(root, out);
out.flush();
}
catch
(IOException e) {
e.printStackTrace();
}
catch
(TemplateException e) {
e.printStackTrace();
}
}
}
|
3>.最终生成的HTML的页面代码如下:
1
|
|
Welcome Big Joe, our beloved leader!
Our latest product: green mouse!
三、如何使用FreeMerker完成Java类代码的自动生成
上面示例 我们的ftl模板文件定义的是HTML页面模板,那么我们将ftl模板定义为Java代码呢 通过数据模板的绑定不就可以生成Java类啦,
下面小吕将利用模板来自动创建实体对象的java类(编写实体类的模板文件相对逻辑简单,但简单归简单,最重要的还是我们要掌握它的思想)
1、属性类型的枚举类 PropertyType.java
1
2
3
4
5
6
7
8
|
/**
* 属性类型枚举类
* @author lvzb.software@qq.com
*
*/
public
enum
PropertyType {
Byte, Short, Int, Long, Boolean, Float, Double, String, ByteArray, Date
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
/**
* 实体对应的属性类
* @author lvzb.software@qq.com
*
*/
public
class
Property {
// 属性数据类型
private
String javaType;
// 属性名称
private
String propertyName;
private
PropertyType propertyType;
public
String getJavaType() {
return
javaType;
}
public
void
setJavaType(String javaType) {
this
.javaType = javaType;
}
public
String getPropertyName() {
return
propertyName;
}
public
void
setPropertyName(String propertyName) {
this
.propertyName = propertyName;
}
public
PropertyType getPropertyType() {
return
propertyType;
}
public
void
setPropertyType(PropertyType propertyType) {
this
.propertyType = propertyType;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
import
java.util.List;
/**
* 实体类
* @author lvzb.software@qq.com
*
*/
public
class
Entity {
// 实体所在的包名
private
String javaPackage;
// 实体类名
private
String className;
// 父类名
private
String superclass;
// 属性集合
List<property> properties;
// 是否有构造函数
private
boolean
constructors;
public
String getJavaPackage() {
return
javaPackage;
}
public
void
setJavaPackage(String javaPackage) {
this
.javaPackage = javaPackage;
}
public
String getClassName() {
return
className;
}
public
void
setClassName(String className) {
this
.className = className;
}
public
String getSuperclass() {
return
superclass;
}
public
void
setSuperclass(String superclass) {
this
.superclass = superclass;
}
public
List<property> getProperties() {
return
properties;
}
public
void
setProperties(List<property> properties) {
this
.properties = properties;
}
public
boolean
isConstructors() {
return
constructors;
}
public
void
setConstructors(
boolean
constructors) {
this
.constructors = constructors;
}
}</property></property></property>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package
${entity.javaPackage};
/**
* This code is generated by FreeMarker
* @author lvzb.software@qq.com
*
*/
public
class
${entity.className}<#
if
entity.superclass?has_content>
extends
${entity.superclass} <!--#
if
-->
{
/********** attribute ***********/
<#list entity.properties as property>
private
${property.javaType} ${property.propertyName};
<!--#list-->
/********** constructors ***********/
<#
if
entity.constructors>
public
${entity.className}() {
}
public
${entity.className}(<#list entity.properties as property>${property.javaType} ${property.propertyName}<#
if
property_has_next>, <!--#
if
--><!--#list-->) {
<#list entity.properties as property>
this
.${property.propertyName} = ${property.propertyName};
<!--#list-->
}
<!--#
if
-->
/********** get/set ***********/
<#list entity.properties as property>
public
${property.javaType} get${property.propertyName?cap_first}() {
return
${property.propertyName};
}
public
void
set${property.propertyName?cap_first}(${property.javaType} ${property.propertyName}) {
this
.${property.propertyName} = ${property.propertyName};
}
<!--#list-->
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
import
java.io.File;
import
java.io.FileWriter;
import
java.io.IOException;
import
java.io.OutputStreamWriter;
import
java.io.Writer;
import
java.util.ArrayList;
import
java.util.HashMap;
import
java.util.List;
import
java.util.Map;
import
freemarker.template.Configuration;
import
freemarker.template.DefaultObjectWrapper;
import
freemarker.template.Template;
import
freemarker.template.TemplateException;
/**
* 自动生成实体类客户端
* @author lvzb.software@qq.com
*
*/
public
class
EntityGeneratorClient {
private
static
File javaFile =
null
;
public
static
void
main(String[] args) {
Configuration cfg =
new
Configuration();
try
{
// 步骤一:指定 模板文件从何处加载的数据源,这里设置一个文件目录
cfg.setDirectoryForTemplateLoading(
new
File(./template));
cfg.setObjectWrapper(
new
DefaultObjectWrapper());
// 步骤二:获取 模板文件
Template template = cfg.getTemplate(entity.ftl);
// 步骤三:创建 数据模型
Map<string, object=
""
> root = createDataModel();
// 步骤四:合并 模板 和 数据模型
// 创建.java类文件
if
(javaFile !=
null
){
Writer javaWriter =
new
FileWriter(javaFile);
template.process(root, javaWriter);
javaWriter.flush();
System.out.println(文件生成路径: + javaFile.getCanonicalPath());
javaWriter.close();
}
// 输出到Console控制台
Writer out =
new
OutputStreamWriter(System.out);
template.process(root, out);
out.flush();
out.close();
}
catch
(IOException e) {
e.printStackTrace();
}
catch
(TemplateException e) {
e.printStackTrace();
}
}
/**
* 创建数据模型
* @return
*/
private
static
Map<string, object=
""
> createDataModel() {
Map<string, object=
""
> root =
new
HashMap<string, object=
""
>();
Entity user =
new
Entity();
user.setJavaPackage(com.study.entity);
// 创建包名
user.setClassName(User);
// 创建类名
user.setConstructors(
true
);
// 是否创建构造函数
// user.setSuperclass(person);
List<property> propertyList =
new
ArrayList<property>();
// 创建实体属性一
Property attribute1 =
new
Property();
attribute1.setJavaType(String);
attribute1.setPropertyName(name);
attribute1.setPropertyType(PropertyType.String);
// 创建实体属性二
Property attribute2 =
new
Property();
attribute2.setJavaType(
int
);
attribute2.setPropertyName(age);
attribute2.setPropertyType(PropertyType.Int);
propertyList.add(attribute1);
propertyList.add(attribute2);
// 将属性集合添加到实体对象中
user.setProperties(propertyList);
// 创建.java类文件
File outDirFile =
new
File(./src-template);
if
(!outDirFile.exists()){
outDirFile.mkdir();
}
javaFile = toJavaFilename(outDirFile, user.getJavaPackage(), user.getClassName());
root.put(entity, user);
return
root;
}
/**
* 创建.java文件所在路径 和 返回.java文件File对象
* @param outDirFile 生成文件路径
* @param javaPackage java包名
* @param javaClassName java类名
* @return
*/
private
static
File toJavaFilename(File outDirFile, String javaPackage, String javaClassName) {
String packageSubPath = javaPackage.replace(
'.'
,
'/'
);
File packagePath =
new
File(outDirFile, packageSubPath);
File file =
new
File(packagePath, javaClassName + .java);
if
(!packagePath.exists()){
packagePath.mkdirs();
}
return
file;
}
}</property></property></string,></string,></string,></string,>
|
效果图如下:
--- 运行后 --->
<程序运行前目录结构> <程序运行后目录结构>
自动生成的实体类User.java 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package
com.study.entity;
/**
* This code is generated by FreeMarker
* @author lvzb.software@qq.com
*
*/
public
class
User
{
/********** attribute ***********/
private
String name;
private
int
age;
/********** constructors ***********/
public
User() {
}
public
User(String name,
int
age) {
this
.name = name;
this
.age = age;
}
/********** get/set ***********/
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
int
getAge() {
return
age;
}
public
void
setAge(
int
age) {
this
.age = age;
}
}
|
四、背后的思考
通过上面两个简单的示例我们了解到所谓的自动生成代码其实就是:
1、定义java类模板文件 2、定义模板数据 3、引用模板文件(.ftl)与模板数据合并生成Java类。
上面的示例中 有的朋友可能会问不就是要编写一个实体对象吗?干嘛搞那么麻烦、又建.ftl文件、又写了那么多类、定义模板数据的过程也是那么麻烦、我还不如手动去写、声明几个属性、set/get快捷键一下子就编写好啦。 真的是这样吗?从一个辅助工具和软件架构的方面去思考,假设做成一个开发的辅助工具或是插件去完成实体类和对应DAO类的自动生成。假设需要建10个实体类和对应含有增删改查基本操作的DAO类。我在C/S客户端上填写包名、类名、属性字段等信息 然后一键生成,想想那是多么爽、多么痛快的一件事(当然 前提是你的模板类要编写的非常强大、通用),而你也许还在不停的 Ctrl+C、Ctrl+V。
五、其他
关于如何编写.ftl模板文件、就需要自己去翻阅资料自我学习啦!