freemarker

概述

FreeMarker是一款模板引擎,即能够基于模板和要改变的数据,来生成输出文本(比如html网页、电子邮件、配置文件、源代码等)的通用工具。

image-20250522223558537

入门案例

springboot整合freemarker,生成的静态文件作为mvc视图返回

导入依赖

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
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<version>2.3.9.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.3.9.RELEAS</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.1</version>
</dependency>

<!-- apache 对 java io 的封装工具库 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>

设置配置文件

application.yml

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: freeMarker-demo
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
suffix: .ftl #指定Freemarker模板文件的后缀名
server:
port: 9000

创建模板文件

resources下创建templates,此目录为freemarker的默认模板存放目录

templates文件夹下创建模板文件f1.ftl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<b>普通文本 String 展示:</b><br><br>
Hello ${name} <br>
<hr>
<b>对象Student中的数据展示:</b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>
</body>
</html>

创建模板类

1
2
3
4
5
6
7
@Data
public class Student {
private String name;//姓名
private int age;//年龄
private Date birthday;//生日
private Float money;//钱包
}

编写controller层代码

freeMarker生成的静态文件作为视图返回

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class FreeMarkerController {

@GetMapping("/fHello")
public String fHello(Model model){
model.addAttribute("name","李白");
Student stu = new Student();
stu.setName("张飞");
stu.setAge(28);
model.addAttribute("stu",stu);
return "f1";
}
}

访问页面

访问http://localhost:9000/fHello

image-20250522230657087

原理分析

image-20250522231733356

image-20250522231807925

image-20250522231830581

image-20250522231908485

可以通过在application.yml中更改默认文件夹和文件后缀名

freeMarker的指令和语法

基础语法

注释

<#-- -->,介于其之间的内容会被freemarker忽略

1
<#-- 注释部分 -->

插值

${}部分的内容,freemarker会用真实值去替换

1
Hello ${name}

FTL指令

html中的标签类似,名字前加#以示区分,freemarker会解析标签中的表达式或逻辑

1
<#指令名>FTL指令</#指令名>

文本

直接写的内容,是表示仅文本信息,这些内容不会被freemarker解析,而是直接将内容输出

常用指令

集合指令—List

<#list></#list>

实例
Java代码
1
2
3
4
5
6
7
8
@GetMapping("/fList")
public String fList(Model model){
List<Student> list = new ArrayList<>();
list.add(new Student("李白",28,new Date(),27.92f));
list.add(new Student("曹操",68,new Date(),2007.92f));
model.addAttribute("stus",list);
return "list";
}
模板内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<h2>学生信息列表</h2>
<table>
<tr>
<th>序号</th>
<th>姓名</th>
<th>年龄</th>
<th>生日</th>
<th>钱包</th>
</tr>
<#list stus as stu> <#--stu为stus里面的每一个元素 -->
<tr>
<td>${stu_index+1}</td> <#-- stu_index表示当前元素索引 -->
<td>${stu.name}</td>
<td>${stu.age}</td>
<td>${stu.birthday?datetime}</td> <!-- 日期类型数据必须指定数据类型-->
<td>${stu.money}</td>
</tr>
</#list>
</table>

哈希数据—map

如何获取map里面元素的值
  • 方式一:map[keyname].properties
  • 方式二:map.keyname.properties

实例

1
2
3
4
5
6
7
8
9
10
@GetMapping("/fMap")
public String fMap(Model model){
Map<String,Student> map = new HashMap<>();
Student stu1 = new Student("李白",28,new Date(),27.92f);
Student stu2 = new Student("曹操",68,new Date(),2007.92f);
map.put("stu1",stu1);
map.put("stu2",stu2);
model.addAttribute("stusMap",map);
return "map";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<h2>集合元素</h2>
<hr/>
<h4>学生1的信息</h4>
<p>name:${stusMap.stu1.name}</p>
<p>age:${stusMap.stu1.age}</p>
<p>birthday:${stusMap.stu1.birthday?datetime}</p>
<p>money:${stusMap.stu1.money}</p>

<h4>学生2的信息</h4>
<p>name:${stusMap["stu2"].name}</p>
<p>age:${stusMap["stu2"].age}</p>
<p>birthday:${stusMap["stu2"].birthday?datetime}</p>
<p>money:${stusMap["stu2"].money}</p>
遍历map
  • <#list>map?keys as key</#list>

map?keys获取map所有的键列表

实例

1
2
3
4
5
6
7
8
9
10
@GetMapping("/fMapLsit")
public String fMapList(Model model){
Map<String,Student> map = new HashMap<>();
Student stu1 = new Student("李白",28,new Date(),27.92f);
Student stu2 = new Student("曹操",68,new Date(),2007.92f);
map.put("stu1",stu1);
map.put("stu2",stu2);
model.addAttribute("stusMap",map);
return "mapList";
}
1
2
3
4
5
6
7
8
9
10
<h2>集合元素</h2>
<hr/>

<#list stusMap?keys as key>
<h4>学生${key_index+1}的信息</h4>
<p>name:${stusMap[key].name}</p>
<p>age:${stusMap[key].age}</p>
<p>birthday:${stusMap[key].birthday?datetime}</p>
<p>money:${stusMap[key].money}</p>
</#list>

if指令

1
2
3
4
5
<#if 条件语句>

<#else>

<#if>

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<h2>集合元素</h2>
<hr/>

<#list stusMap?keys as key>
<#if stusMap[key].age gte 40> <#-- 年龄大于40的用红色表示 -->
<div style="color: red">
<h4>学生${key_index+1}的信息</h4>
<p>name:${stusMap[key].name}</p>
<p>age:${stusMap[key].age}</p>
<p>birthday:${stusMap[key].birthday?datetime}</p>
<p>money:${stusMap[key].money}</p>
</div>
<#else>
<div>
<h4>学生${key_index+1}的信息</h4>
<p>name:${stusMap[key].name}</p>
<p>age:${stusMap[key].age}</p>
<p>birthday:${stusMap[key].birthday?datetime}</p>
<p>money:${stusMap[key].money}</p>
</div>
</#if>

</#list>

运算符

算数运算符

  • 加法: +
  • 减法: -
  • 乘法: *
  • 除法: /
  • 求模 (求余): %

注意:

除了 + 运算以外,其他的运算只能和 number 数字类型的计算

比较运算符

  • =或者==:判断两个值是否相等.
  • !=:判断两个值是否不等.
  • >或者gt:判断左边值是否大于右边值
  • >=或者gte:判断左边值是否大于等于右边值
  • <或者lt:判断左边值是否小于右边值
  • <=或者lte:判断左边值是否小于等于右边值

注意:

  • =!=可以用于字符串、数值和日期来比较是否相等
  • =!=两边必须是相同类型的值,否则会产生错误
  • 字符串 "x""x ""X"比较是不等的.因为FreeMarker是精确比较
  • 其它的运行符可以作用于数字和日期,但不能作用于字符串
  • 使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字符
  • 可以使用括号来避免这种情况,如:<#if (x>y)>

逻辑运算符

  • 逻辑与:&&
  • 逻辑或:||
  • 逻辑非:!

注意:

逻辑运算符只能作用于布尔值,否则将产生错误

空值处理

判断某变量是否存在使用 ??

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
<h2>集合元素</h2>
<hr/>

<#if stusMap??> <#--判断stusMap是否存在-->
<#list stusMap?keys as key>
<#if (stusMap[key].age >= 40)>
<div style="color: red">
<h4>学生${key_index+1}的信息</h4>
<p>name:${stusMap[key].name}</p>
<p>age:${stusMap[key].age}</p>
<p>birthday:${stusMap[key].birthday?datetime}</p>
<p>money:${stusMap[key].money}</p>
</div>
<#else>
<div>
<h4>学生${key_index+1}的信息</h4>
<p>name:${stusMap[key].name}</p>
<p>age:${stusMap[key].age}</p>
<p>birthday:${stusMap[key].birthday?datetime}</p>
<p>money:${stusMap[key].money}</p>
</div>
</#if>

</#list>
</#if>

缺失变量默认值使用 !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<#-- 注释部分 -->
<b>普通文本 String 展示:</b><br><br>
Hello ${name!'王虎'} <br> <#--当变量name不存在时,默认替换为王虎-->
<hr>
<b>对象Student中的数据展示:</b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>
</body>
</html>

内建函数

内建函数语法格式: 变量+?+函数名称

获取某个集合的大小

${集合名?size}

日期相关

显示年月日: ${today?date}
显示时分秒:${today?time}
显示日期+时间:${today?datetime}
自定义格式化: ${today?string("yyyy年MM月")}

内建函数c

model.addAttribute(“point”, 102920122);

point是数字型,使用${point}会显示这个数字的值,每三位使用逗号分隔。

如果不想显示为每三位分隔的数字,可以使用c函数将数字型转成字符串输出

${point?c}

将json字符串转成对象

text?eval

1
2
3
4
<#-- assign的作用是创建一个新的变量 -->
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#assign data=text?eval />
开户行:${data.bank} 账号:${data.account}

freeMaker静态文件生成

mvc中支持将freemarker静态文件作为视图返回前端,但是在一些场景中,需要将生成的静态文件输出存储在本地

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
package com.bang;


import com.bang.entity.Student;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@SpringBootTest
public class TestFreeMarker {

@Autowired
private Configuration configuration;

@Test
public void testGenerateFile() throws IOException, TemplateException {
//1.加载静态模板文件
Template template = configuration.getTemplate("mapList.ftl");

//2.准备数据
Map<String, Student> map = new HashMap<>();
Student stu1 = new Student("李白",28,new Date(),27.92f);
Student stu2 = new Student("曹操",68,new Date(),2007.92f);
map.put("stu1",stu1);
map.put("stu2",stu2);

Map<String, Object> params = new HashMap<>();
params.put("stusMap",map);
//3.添加数据模型,输出静态文件
//第一个参数 数据模型,数据模型以HashMap的形式传入
//第二个参数 输出流
template.process(params, new FileWriter("./test.html"));
}
}