快速入门FreeMarker

本文最后更新于:December 8, 2022 pm

FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

目录

FreeMarker中文手册

本文仅记录了在Java中的一些基础使用。

快速了解

这里以一个Demo为例,快速生成一个自定义模板的Java类。这里将其方法封装成一个方法,方便后续学习和使用,后续生成模板时均是调用此方法生成。方法如下:

依赖

1
2
3
4
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>

GenerateTemplateApi.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
package com.tothefor.motorcode.component.GenerateTemplate;

import freemarker.template.Configuration;

import java.util.Map;

/**
* @Author DragonOne
* @Date 2022/11/22 13:53
* @Title
* @Description
*/
public interface GenerateTemplateApi {

/**
* @param baseTemplatePath 模版所在目录,如 src/main/resources/freemarker/project
* @param templateFile 模版文件全名称 如 test.ftl
* @param classPath 生成的类存放目录路径
* @param className 生成的类名
* @param dataMap 模版中需要的数据
* @param <T> void
*/
<T> void generateTemplate(String baseTemplatePath, String templateFile, String classPath, String className, Map<String, T> dataMap);

}

GenerateTemplateApiImpl.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
43
44
45
46
47
48
package com.tothefor.motorcode.component.GenerateTemplate;

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileWriter;
import java.util.Map;

/**
* @Author DragonOne
* @Date 2022/11/22 13:54
* @Title
* @Description
*/
@Service
public class GenerateTemplateApiImpl implements GenerateTemplateApi {

private static final Configuration FREE_MAKER_CONFIG = new Configuration(Configuration.VERSION_2_3_28);

@Override
public <T> void generateTemplate(String baseTemplatePath, String templateFile, String classPath, String className, Map<String, T> dataMap) {
FREE_MAKER_CONFIG.setDefaultEncoding("UTF-8");

FileWriter fileWriter = null;
try {
FREE_MAKER_CONFIG.setDirectoryForTemplateLoading(new File(baseTemplatePath));
Template template = FREE_MAKER_CONFIG.getTemplate(templateFile);
File file = new File(classPath + "/" + className + ".java");
fileWriter = new FileWriter(file);
template.process(dataMap, fileWriter);
fileWriter.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (Exception e) {
e.printStackTrace();
}

}
}
}

测试

这里简单用一个模板进行测试:

test.ftl

1
2
3
4
5
6
package ${package};



String PROJECT = "${title}";

然后,调用上面的方法,传入对应的参数。

1
2
3
4
5
6
7
8
9
10
11
12
@Test
void testFtl() {
String baseTemplatePath = "src/main/resources/freemarker/project"; // 模板位置,注意最后没有斜杠
String filePath = "test.ftl";
String classPath = "src/main/java/com/tothefor";
HashMap<String, String> map = new HashMap<>();
map.put("package", "com.tothefor"); // 对应模板里面的动态参数名称
map.put("title", "totheforsssss");

// 调用方法生成tttt.java类。自行修改调用
GenerateTemplateApi().generateTemplate(baseTemplatePath, filePath, classPath, "ttttt", map);
}

最后就可以在对应位置看见生成的Java类了。

数据类型

FreeMarker模板中的数据类型有以下几种,以及对应Java中的类型:

  • 布尔值:对应Java中的Boolean类型。但是不能直接输出,需要转换为字符串输出。
  • 日期型:对应Java中的Date类型。但是不能直接输出,需要转换为字符串输出。
  • 数值型:对应Java中的int、float、double等数值类型。有三种显示形式:数值型(默认)、货币型、百分比型。
  • 字符型:对应Java中的字符串,有很多内置函数。
  • sequence类型:对应Java中的数组,list、set等集合类型。
  • hash类型:对应Java中的Map类型。

布尔类型

不能直接输出,则需要先转换为字符串再输出(借助内建函数),所以有两种方式:

  • 方式一:?c
  • 方式二:?string 或者 ?string(“true时的文本”,”false时的文本”)

更多用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package ${package};

/**
* @Author DragonOne
* @Date 2022/12/4 14:13
* @墨水记忆 www.tothefor.com
*/
public class ttttt {
public static void main(String[] args) {
String PROJECT = "${title}";

boolean flag = ${flag};
if(flag){
System.out.println("yes");
}else{
System.out.println("no");
}
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
@Test
void testMain() {
String baseTemplatePath = "src/main/resources/freemarker/project";
String filePath = "test.ftl";
String classPath = "src/main/java/com/tothefor";
HashMap<String, Object> map = new HashMap<>();
map.put("package", "com.tothefor");
map.put("title", "totheforsssss");
map.put("flag", "true"); // 这种方式只是字符串,并不是真正的布尔值,但是在模板的使用位置,也可以当成是true。如传入的是一个逻辑表达式,则必须转换为字符串进行输出。
GenerateTemplateApi().generateTemplate(baseTemplatePath, filePath, classPath, "ttttt", map);
}

日期类型

更多用法

自动生成

  • ${.now} :自动生成当前时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package ${package};
/**
* @Author DragonOne
* @Date ${.now}
* @墨水记忆 www.tothefor.com
*/
public class ttttt {
public static void main(String[] args) {
String PROJECT = "${title}";

String Tests="${flag}";
boolean flag = true;
if(flag){
System.out.println("yes");
}else{
System.out.println("no");
}
}
}

生成效果:

1
2
3
4
5
/**
* @Author DragonOne
* @Date 2022-12-4 14:35:29
* @墨水记忆 www.tothefor.com
*/

指定时间

1
2
3
4
5
/**
* @Author DragonOne
* @Date ${DATE?date}
* @墨水记忆 www.tothefor.com
*/

传入值:

1
map.put("DATE", new Date());

效果:

1
2
3
4
5
/**
* @Author DragonOne
* @Date 2022-12-4
* @墨水记忆 www.tothefor.com
*/

更多用法

不能使用:${DATE?datetime(“yyyy-MM-dd HH:mm:ss”)}。因为datetime的类型在月份只有一位数时,显示的就是一位数,而转换需要两位数(前面补0)。可见下面用例的区别。

1
2
3
4
5
6
7
8
/**
* @Author DragonOne
* @Date ${DATE?date}
* @Date ${DATE?time}
* @Date ${DATE?datetime}
* @Date ${DATE?string("yyyy-MM-dd HH:mm:ss")}
* @墨水记忆 www.tothefor.com
*/

效果:

1
2
3
4
5
6
7
8
/**
* @Author DragonOne
* @Date 2022-12-4
* @Date 14:54:32
* @Date 2022-12-4 14:54:32
* @Date 2022-12-04 14:54:32
* @墨水记忆 www.tothefor.com
*/

数值类型

可以直接输出,也可以转换成字符串输出。

  • 转普通字符串:?c
  • 转货币型字符串:?string.currency
  • 转百分比型字符串:?string.percent
  • 保留浮点数,指定小数(#表示一个小数位,两个表示保留两位小数):?string[“0.##”]
1
2
3
4
5
6
7
8
String s1="${numA}";
String s2="${numB}";
String s3="${numB?c}";
String s4="${numB?string.currency}";
String s5="${numC?c}";
String s6="${numC?string.currency}";
String s7="${numC?string.percent}";
String s8="${numC?string["0.##"]}";

效果:(会四舍五入)

1
2
3
4
5
6
7
8
String s1="11";
String s2="12.34";
String s3="12.34";
String s4="¥12.34";
String s5="0.3456";
String s6="¥0.35";
String s7="35%";
String s8="0.35";

字符串类型

更多用法

常用函数:

  • 截取字符串(左闭右开):?substring(start,end)。

  • 首字母小写输出:?uncap_first。

  • 首字母大写输出:?cap_first。

  • 字母转小写输出:?lower_case。

  • 字母转大写输出:?upper_case。

  • 获取字符串长度:?length。

  • 是否以指定字符开始(布尔类型):?starts_with(“xxx”)。

  • 是否以指定字符结尾(布尔类型):?ends_with(“xxx”)。

  • 获取指定字符的索引:?index_of(“xx”)。

  • 去除字符串前后空格:?trim。

  • 替换指定字符串:?replace(“xx”,”sss”)

空值处理

FreeMarker的变量必须赋值,否则会抛出异常。对于FreeMarker来说,null值和不存在的变量是一样的,因为FreeMarker无法理解null值。FreeMarker提供了两个运算符来避免空值:

  • !:指定缺失变量的默认值。
    ${value!}:如果value值为空,则默认是空字符串。
    ${value!”默认值”}:如果value值为空,则使用默认值。

  • ??:判断变量是否存在

    如果变量存在,返回true,否则返回false。

    ${(value??)string}

示例:

1
2
3
4
5
6
String nullStrA="${nullStrA!}";
String nullStrAA="${nullStrA!"nullStrA默认值"}";
String nullStrAAA="${(nullStrA??)?string}";
String nullStrB="${nullStrB!}";
String nullStrBB="${nullStrB!"nullStrB默认值"}";
String nullStrBBB="${(nullStrB??)?string}";

传入值:

1
2
map.put("nullStrA",null);
map.put("nullStrB","");

效果:

1
2
3
4
5
6
String nullStrA="";
String nullStrAA="nullStrA默认值";
String nullStrAAA="false";
String nullStrB="";
String nullStrBB="";
String nullStrBBB="true";

sequence类型

适用于:数组、List等。

介绍:

1
2
3
4
5
6
7
8
9
10
11
12
13
<#list 序列名 as 元素名>
${元素名}
</#list>
获取序列长度:${序列名?size}
获取序列元素的下标:${元素名?index}
获取第一个元素:${序列名?first}
获取最后一个元素:${序列名?last}

倒序输出 序列名?reverse
升序输出 序列名?sort
降序输出 序列名?sort?reverse
指定字段名排序 序列名?sort_by("字段名")
注意:JavaBean集合,对应的字段名需要提供get方法

插入数据:

1
2
String[] strings = new String[]{"aaaa","bbbb","cccc","dddd"};
map.put("stringArray",strings);

模板使用:

1
2
3
4
5
6
7
<#list stringArray as it>
// 下标为 ${it?index}
String ${it} = "${it}";
</#list>
// 元素个数为:${stringArray?size}
// 第一个元素为:${stringArray?first}
// 最后一个元素为:${stringArray?last}

效果:

1
2
3
4
5
6
7
8
9
10
11
// 下标为 0
String aaaa = "aaaa";
// 下标为 1
String bbbb = "bbbb";
// 下标为 2
String cccc = "cccc";
// 下标为 3
String dddd = "dddd";
// 元素个数为:4
// 第一个元素为:aaaa
// 最后一个元素为:dddd

其他操作

模板使用:

1
2
3
4
<#list stringArray?reverse as it>
// 下标为 ${it?index}
String ${it} = "${it}";
</#list>

效果实现:

1
2
3
4
5
6
7
8
// 下标为 0
String dddd = "dddd";
// 下标为 1
String cccc = "cccc";
// 下标为 2
String bbbb = "bbbb";
// 下标为 3
String aaaa = "aaaa";

其他操作同理,如:

1
2
3
4
5
6
7
8
9
<#list stringArray?sort as it>
// 下标为 ${it?index}
String ${it} = "${it}";
</#list>

<#list stringArray?sort?reverse as it>
// 下标为 ${it?index}
String ${it} = "${it}";
</#list>

如果是JavaBean,指定字段排序:(假定类有age字段)

1
2
3
4
<#list stringArray?sort_by("age") as it>
// 下标为 ${it?index}
String ${it} = "${it}";
</#list>

Hash类型

使用Hash类型。有两种遍历方式:key遍历输出、value遍历输出。

介绍:

1
2
3
4
5
6
7
8
9
key遍历输出:
<#list hash?keys as key>
${key} -- ${hash[key]}
</#list>

value遍历输出:
<#list hash?values as value>
${value}
</#list>

添加数据:

1
2
3
4
5
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("aaa", 111);
hashMap.put("bbb", 222);
hashMap.put("ccc", 333);
map.put("hashMap", hashMap);

模板使用:

1
2
3
4
5
6
<#list hashMap?keys as key>
${key} -- ${hashMap[key]}
</#list>
<#list hashMap?values as value>
#{value}
</#list>

效果实现:

1
2
3
4
5
6
aaa -- 111
ccc -- 333
bbb -- 222
111
333
222