Freemarker 完整讲解(后端Java模板引擎)
一、Freemarker 是什么
FreeMarker 是一款Java 后端模板引擎,纯 Java 编写,不需要依赖 Servlet 容器,核心作用:
-
分离数据(Java 后端) 和 视图(HTML / 文本 / 邮件 / 代码文件);
-
后端把对象、集合、数字、字符串传给模板,Freemarker 语法渲染输出最终文本;
-
常见场景:
-
前后不分离项目渲染 HTML 页面(老管理系统、后台)
-
动态生成邮件、短信模板
-
自动生成代码(MyBatis Generator、代码生成器大量使用)
-
导出动态 Word、Excel、静态页面
-
和 Thymeleaf/JSP 对比
-
JSP:耦合 Tomcat,编译成 Servlet,前后代码混写;
-
Freemarker:纯模板文件
.ftl,无容器依赖,可离线渲染; -
Thymeleaf:兼顾原型,Freemarker 更轻量、语法简洁,代码生成场景首选。
二、环境依赖(SpringBoot 最常用)
Maven 依赖
xml
<!-- freemarker 核心 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<!-- SpringBoot 自动装配(可选,简化配置) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
SpringBoot 基础配置 application.yml
yaml
spring:
freemarker:
# 模板文件存放目录,默认 resources/templates/
template-loader-path: classpath:/templates/
suffix: .ftl # 模板后缀
charset: UTF-8
cache: false # 开发关闭缓存,实时刷新模板
settings:
number_format: 0.## # 数字格式化
模板文件统一放:resources/templates/xxx.ftl
三、核心语法 + 实战示例
基础规则
-
${变量名}:输出变量值; -
<#if>、<#else>:判断; -
<#list 集合 as item>:循环遍历; -
<#assign>:定义局部变量; -
?:内置函数,处理空值、字符串、日期; -
#开头是指令,$是取值,@是自定义宏。
示例 1:基础取值、空值处理、日期、数字格式化
1. Java Controller 传数据
java
@Controller
@RequestMapping("/fm")
public class FreemarkerDemoController {
@GetMapping("/demo1")
public String demo1(Model model) {
// 普通字符串
model.addAttribute("username", "张三");
// 数字
model.addAttribute("price", 128.567);
// 日期
model.addAttribute("birthDay", new Date());
// 空值变量
model.addAttribute("address", null);
// 对象
User user = new User();
user.setAge(26);
user.setPhone("13800138000");
model.addAttribute("user", user);
return "demo1"; // 对应 templates/demo1.ftl
}
}
// 实体类
class User {
private Integer age;
private String phone;
// get/set 必须要有!Freemarker通过getter取值
}
2. templates/demo1.ftl 模板
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>基础取值示例</title>
</head>
<body>
<h3>1. 普通变量输出</h3>
用户名:${username}
<h3>2. 对象属性(自动调用getAge() getPhone())</h3>
年龄:${user.age},手机号:${user.phone}
<h3>3. 数字格式化</h3>
原价:${price},保留2位小数:${price?string("0.00")}
<h3>4. 日期格式化</h3>
原始日期:${birthDay}
年月日:${birthDay?date("yyyy-MM-dd")}
完整时间:${birthDay?date("yyyy-MM-dd HH:mm:ss")}
<h3>5. 空值安全处理(重要,防止空指针报错)</h3>
<!-- address为空时,显示默认值"未知地址" -->
地址:${address!"未知地址"}
<!-- 判断变量是否存在 -->
<#if address??>
地址存在:${address}
<#else>
地址为空
</#if>
</body>
</html>
访问 /fm/demo1 自动渲染 HTML 页面。
示例 2:if 判断、逻辑分支
Controller 追加参数
java
model.addAttribute("age", 22);
model.addAttribute("vip", true);
ftl 判断代码
html
<h3>if 判断示例</h3>
<#if age lt 18>
未成年,禁止访问
<#elseif age gte 18 && age lt 30>
青年用户
<#else>
中年用户
</#if>
<#if vip>
<span style="color:red">VIP会员</span>
<#else>
普通用户
</#if>
运算符:
gt大于、gte大于等于、lt小于、lte小于等于、==等于、!=不等于
示例 3:list 循环遍历(最常用,列表、表格)
Controller 传 List
java
// 集合
List<User> userList = new ArrayList<>();
userList.add(new User(20,"13111111111"));
userList.add(new User(28,"13222222222"));
userList.add(new User(35,"13333333333"));
model.addAttribute("userList", userList);
ftl 循环渲染表格
html
<h3>用户列表表格</h3>
<table border="1">
<tr>
<th>序号</th>
<th>年龄</th>
<th>手机号</th>
</tr>
<!-- 遍历集合,item单条对象,index下标从0开始 -->
<#list userList as item>
<tr>
<td>${item_index + 1}</td>
<td>${item.age}</td>
<td>${item.phone}</td>
</tr>
<!-- 集合为空时显示 -->
<#else>
<tr>
<td colspan="3">暂无用户数据</td>
</tr>
</#list>
</table>
内置变量:item_index 当前下标、item_has_next 是否有下一条。
示例 4:自定义变量 assign + 宏 macro(复用组件)
1. assign 定义临时变量
html
<#assign pageSize = 10 />
每页条数:${pageSize}
<#assign title = "后台管理系统" />
<title>${title}</title>
2. macro 宏(封装公共组件,类似 Vue 组件)
html
<!-- 定义分页宏,参数 currentPage total -->
<#macro page currentPage total>
<div class="page">
当前第${currentPage}页,总${total}页
<a href="?page=${currentPage - 1}">上一页</a>
<a href="?page=${currentPage + 1}">下一页</a>
</div>
</#macro>
<!-- 调用宏,传参 -->
<@page currentPage=2 total=10 />
示例 5:纯 Java 离线渲染(不依赖 Web,代码生成场景)
不通过 SpringMVC,直接用 Freemarker API 渲染文本 / 文件,代码生成器核心用法:
java
public class FreemarkerUtil {
public static void main(String[] args) throws Exception {
// 1. 配置模板加载路径
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
cfg.setDirectoryForTemplateLoading(new File("templates"));
cfg.setDefaultEncoding("UTF-8");
// 2. 加载模板文件
Template template = cfg.getTemplate("code.ftl");
// 3. 填充数据
Map<String,Object> dataMap = new HashMap<>();
dataMap.put("tableName", "sys_user");
dataMap.put("className", "SysUser");
// 4. 输出到文件(生成Java实体类)
Writer out = new FileWriter("SysUser.java");
template.process(dataMap, out);
out.flush();
out.close();
}
}
模板code.ftl可以写 Java 代码,运行后自动生成实体类文件。
四、常见踩坑点
-
实体类没有 getter:Freemarker 依靠 getter 获取属性,只写 private 字段会报错;
-
空值不加! :
${address}如果 address 为 null,直接抛异常,必须写${address!"默认值"}; -
模板缓存 :开发环境开启
spring.freemarker.cache=false,否则修改 ftl 不生效; -
符号全角 :
< #if>空格不能乱加,指令标签不能有多余空格; -
数字 / 日期格式化 :直接输出日期会显示英文时间戳,必须搭配
?date格式化。
五、适用场景总结
-
传统服务端渲染后台管理页面;
-
代码生成工具(生成 Mapper、Entity、Controller);
-
邮件、短信、消息模板动态拼接;
-
批量生成静态 HTML、Word 文档;
-
离线文本渲染,无需 Web 容器。