一、 使用场景
FreeMarker是一款模板引擎,可以帮助开发者生成任意类型的文本,包括HTML、XML、JSON、CSS、邮件模板等等。它可以将数据和模板进行绑定,通过填充模板中的占位符来生成最终的文本。FreeMarker最常见的应用场景是在Web开发中生成HTML页面。在Web应用中,数据通常来自于后端,而页面的生成则是由前端负责。通过使用FreeMarker,前端可以使用模板来定义页面的结构和样式,后端则可以将数据填充到模板中,生成最终的HTML页面。
总之,只要你有模板,就能通过 FreeMark 去生成对应的内容
二、简单的使用
2.1 pom
<dependencies>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
</dependencies>
2.2 util
package com.apps.util;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.File;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
public class FreeMarkUtil {
//单例
private static final Configuration cfg = new Configuration();
static {
// 设置默认编码
cfg.setDefaultEncoding("utf-8");
}
public static String process(String templateName, Map<String, Object> root) throws Exception {
String path = FreeMarkUtil.class.getResource("").getPath();
//模板的位置,我是放在 /resource 下,我的类路径是 com/apps/util
path = path.replace("/com/apps/util/","");
//建议,就算不同文件夹,但是模板文件名也不能相同
FileTemplateLoader ftl = new FileTemplateLoader(new File(path +"/component_template/"));
TemplateLoader[] loaders = new TemplateLoader[] { ftl };
MultiTemplateLoader mul = new MultiTemplateLoader(loaders);
cfg.setTemplateLoader(mul);
//创建模版对象
Template t = cfg.getTemplate(templateName+".json");
//设置输出流
StringWriter writer = new StringWriter();
String result = "";
try {
//在模版上执行插值操作,并输出到制定的输出流中
t.process(root, writer);//root 是模板中的变量的值
result = writer.toString();
}catch (Exception e){
throw new Exception(e);
}finally {
writer.close();
}
return result;
}
// 测试
public static void main(String[] args) throws Exception {
HashMap<String, Object> root = new HashMap<>();
String template = process("template", root);
System.out.println(template);
}
}
用 String process = FreeMarkUtil.process(name, params); 就能拿到 String 对象,就可以转换成自己想要的对象。建议用 JSON.parseObject() 去转换成对象,因为模板大了之后其他工具包可以转换不过来。
2.3 示例
我的模板是一个简单的 Json
使用 main() 生成的如下:
三、 Freemarker 进阶使用
3.1 变量表达式
在 FreeMarker 中,可以使用以下变量表达式来引用模板中的变量或属性:
1. ${variable}:输出变量的值。
2. ${object.property}:输出对象的属性值。
3. ${map.key}:输出 Map 中指定 key 的值。
4. ${list[index]}:输出列表中指定位置的值。
5. ${variable!‘defaultValue’}:如果变量值为 null,则输出 defaultValue 值。记得有单引号
6. ${variable?size}:输出变量的长度(对于字符串、列表或数组)或元素个数(对于 Map)。
7. ${variable?keys}:输出变量中所有的键(对于 Map)。
8. ${variable?values}:输出变量中所有的值(对于 Map)。
9. ${variable?string}:将变量转换为字符串输出 (没啥用,你存什么格式,就会填什么格式)
10. ${variable?html}:将变量转义为 HTML 格式输出。(没啥用,你存什么格式,就会填什么格式)
11. ${variable?cap_first}:将变量的首字母大写后输出。
12. ${variable?lower_case}:将变量转换为小写字母后输出。
13. ${variable?upper_case}:将变量转换为大写字母后输出。
14. ${variable?date}:将变量转换为日期格式输出。
15. ${variable?number}:将变量转换为数字格式输出。
16. ${variable?c}:将变量格式化为货币金额输出。(没啥用)
17. ${variable?string("UTF-8")}:将变量转换为指定编码格式的字符串输出。(没啥用,换模板的时候设置 UTF-8就可以了)
这些变量表达式是 FreeMarker 中最常用的,通过使用这些表达式可以方便地在模板中输出变量的值、属性、长度等信息,并进行格式化输出。
3.2 内置指令
FreeMarker 内置指令包括:
1. if、elseif、else:用于条件语句的指令。
2. list、else、items:用于循环语句的指令。
3. include:用于引用其他模板的指令。
4. macro、nested、return:用于宏定义和调用的指令。
5. assign:用于将变量赋值的指令。
6. global、local:用于全局和局部变量的定义和赋值。
7. function:用于定义自定义函数的指令。
8. switch、case、default:用于多分支条件语句的指令。
9. break、stop、recover、rt、escape:用于流程控制的指令。
10. compress、lt、rt、noparse:用于模板格式化和编译优化的指令。
这些指令可以实现复杂的模板逻辑和数据处理,提高模板的可读性和可维护性。
3.3 条件指令
FreeMarker 内置了多种条件指令,常用的包括:
1. `if`:判断是否满足条件,满足则执行相应的语句块,语法如下:
<#if condition>
statement1
statement2
<#elseif condition2>
statement3
<#else>
statement4
</#if>
其中,`condition` 和 `condition2` 为条件表达式,如果满足则执行 `statement1` 和 `statement2`,如果不满足则判断是否满足 `condition2`,满足则执行 `statement3`,否则执行 `statement4`。
2. `switch`:根据表达式的值进行多条件分支,语法如下:
<#switch expression>
<#case value1>
statement1
<#case value2>
statement2
<#default>
statement3
</#switch>
其中,`expression` 为表达式,`value1`、`value2` 为需要匹配的值,如果匹配则执行相应的语句块,如果都不匹配则执行 `statement3`。
3. `list`:循环遍历一个列表,语法如下:
<#list sequence as item>
statement1
statement2
</#list>
其中,`sequence` 为要遍历的列表,`item` 为当前遍历的元素变量名,`statement1` 和 `statement2` 为要执行的语句块。
4. `foreach`:与 `list` 类似,用于遍历一个集合或数组,语法如下:
<#foreach item in sequence>
statement1
statement2
</#foreach>
其中,`sequence` 为要遍历的集合或数组,`item` 为当前遍历的元素变量名,`statement1` 和 `statement2` 为要执行的语句块。
5. `assign`:给变量赋值,语法如下:
<#assign variable=value>
其中,`variable` 为变量名,`value` 为要赋的值。
这些条件指令可以方便地控制模板的渲染流程和输出结果。
3.4 循环指令
FreeMarker 中常见的循环指令有以下几种:
1. `<#list>` 指令:用于遍历一个序列或集合,通常和 `item`、`index` 等内置变量一起使用,示例代码如下:
<#list sequenceOrCollection as item>
${item_index + 1}. ${item}
</#list>
该指令遍历了名为 `sequenceOrCollection` 的序列或集合,并在循环体内使用了 `item` 变量表示当前元素,使用了 `item_index` 变量表示当前元素在序列或集合中的下标(从 0 开始)。
2. `<#foreach>` 指令:与 `<#list>` 类似,用于遍历一个序列或集合,但使用方式略有不同,示例代码如下:
<#foreach item in sequenceOrCollection>
${item_index + 1}. ${item}
</#foreach>
该指令同样遍历了名为 `sequenceOrCollection` 的序列或集合,并在循环体内使用了 `item` 变量表示当前元素,使用了 `item_index` 变量表示当前元素在序列或集合中的下标(从 0 开始)。
3. `<#while>` 指令:用于执行一个条件循环,示例代码如下:
<#assign counter = 0>
<#while counter < 10>
${counter + 1}. Hello, world!<br>
<#assign counter = counter + 1>
</#while>
该指令先将变量 `counter` 初始化为 0,然后执行一个条件循环,在每次循环中输出一条消息,并将 `counter` 加 1,直到 `counter` 大于等于 10 时循环结束。
4. `<#recurse>` 指令:用于执行递归循环,通常与自定义函数一起使用,示例代码如下:
<#function printNodes nodes>
<#list nodes as node>
${node.name}
<#if node.children??>
<ul>
<#recurse nodes=node.children>
</ul>
</#if>
</#list>
</#function>
该指令定义了一个自定义函数 `printNodes`,接受一个 `nodes` 参数,然后遍历 `nodes` 中的所有节点,并递归地输出其子节点的名称,如果当前节点没有子节点,则不输出 `<ul>` 标签。
III. Freemarker进阶
- 自定义指令
FreeMarker允许开发者定义自己的指令。开发者可以自行实现模板指令的功能,以便更好地控制模板的生成。
要定义自己的指令,可以通过实现`TemplateDirectiveModel`接口并覆盖其中的`execute()`方法来实现。在自定义指令中,我们可以通过`Environment`对象访问模板的各个部分,比如输出流和参数。
例如,以下是一个自定义指令的示例,该指令将字符串反转并输出:
public class ReverseDirective implements TemplateDirectiveModel {
public void execute(Environment env, Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException {
// 获取参数
String input = params.get("input").toString();
// 反转字符串
String output = new StringBuilder(input).reverse().toString();
// 输出结果
Writer out = env.getOut();
out.write(output);
}
}
定义好自定义指令后,需要将其注册到FreeMarker的配置中,例如:
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
// 注册自定义指令
cfg.setSharedVariable("reverse", new ReverseDirective());
在模板中使用自定义指令:
<@reverse input="Hello, World!"/>
输出:
!dlroW ,olleH