Freemarker 教程:从入门到精通
目录
- 什么是 Freemarker?
- 为什么选择 Freemarker?
- 核心概念
- 1 模板
- 2 数据模型
- 3 配置
- 4 处理
- 快速上手:第一个 Freemarker 程序
- Freemarker 常用语法详解
- 1 注释
- 2 输出变量:
- 3 逻辑指令:
<#if>,<#list>,<#switch> - 4 循环控制:
<#break>,<#continue> - 5 模板包含:
<#include>,<#import> - 6 宏定义:
<#macro>,<@macro> - 7 命名空间:
<#import> - 8 内建函数
- 9 空值处理: 和
- 10 算术和比较运算
- 最佳实践与注意事项
- 实际应用场景
- 1 生成静态 HTML 页面
- 2 生成 XML/JSON 配置文件
- 3 在 Spring Boot 中使用 Freemarker
- 总结与资源
什么是 Freemarker?
FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出,它的作用就是将数据(Model)和模板(Template)结合起来,生成最终的输出文本(View)。

你可以把它想象成一个“填空题”工具:
- 模板:是带有特殊标记(如
${name})的文本文件(如.ftl文件)。 - 数据:是一个 Java 对象或数据结构,包含了要填入空格的实际内容。
- 引擎:Freemarker 本身,它会读取模板,用数据替换掉所有标记,然后生成最终的纯文本。
工作流程图:
[Java 数据] + [Freemarker 模板] --(Freemarker Engine)--> [最终输出文本]
为什么选择 Freemarker?
- 关注点分离:设计人员(HTML/CSS)和开发人员(Java 逻辑)可以独立工作,设计师修改模板而无需触碰 Java 代码,反之亦然。
- 强大的模板语言:提供了丰富的指令(
if,list,macro等)来处理复杂的逻辑,如循环、条件判断等。 - 与 Web 容器无关:它不依赖于任何 Web 框架,可以单独使用,也可以轻松集成到 Spring, Struts, Servlet 等各种框架中。
- 通用性:不仅可以生成 HTML,还可以生成任何基于文本的格式,如 XML, JSON, CSV, Java 源代码等。
- 性能良好:模板在第一次加载时会被编译成高效的 Java 字节码,后续处理速度非常快。
核心概念
在开始编码前,必须理解 Freemarker 的三个核心组件。
1 模板
模板是使用 Freemarker 标记编写的文本文件,通常以 .ftl (FreeMarker Template) 作为后缀。

示例 hello.ftl:
<html>
<head><title>欢迎</title></head>
<body>
<h1>你好,${user}!</h1>
<p>当前时间是:${.now?string("yyyy-MM-dd HH:mm:ss")}</p>
</body>
</html>
这里的 ${user} 和 ${.now} Freemarker 的标记。
2 数据模型
数据模型是 Java 端提供的数据,它是一个树形结构,最简单的形式是 java.util.Map,但更常用的是 freemarker.template.Configuration 所能接受的任何对象,如 POJO (普通 Java 对象)。
示例数据模型:

Map<String, Object> dataModel = new HashMap<>();
dataModel.put("user", "张三");
// 也可以放入整个对象
// dataModel.put("currentUser", new User("张三", 30));
3 配置
Configuration 类是 Freemarker 的核心,它负责管理模板的加载路径、编码、版本等全局设置。
示例配置:
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
// 设置模板所在的目录
cfg.setDirectoryForTemplateLoading(new File("src/main/resources/templates"));
// 设置模板文件编码
cfg.setDefaultEncoding("UTF-8");
4 处理
处理过程就是将数据模型和模板结合,生成最终输出的过程,通过 Template 对象的 process 方法完成。
Template template = cfg.getTemplate("hello.ftl");
Writer out = new FileWriter("output.html");
template.process(dataModel, out);
out.close();
快速上手:第一个 Freemarker 程序
让我们创建一个简单的 Java 程序来生成一个欢迎页面。
添加依赖
如果你使用 Maven,在 pom.xml 中添加:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version> <!-- 建议使用最新稳定版 -->
</dependency>
创建模板文件
在 src/main/resources/templates 目录下创建 welcome.ftl:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">欢迎页</title>
</head>
<body>
<h2>欢迎, ${name}!</h2>
<p>您的角色是: ${role}</p>
</body>
</html>
编写 Java 代码
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class FreemarkerDemo {
public static void main(String[] args) {
// 1. 创建 Configuration 实例
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
try {
// 2. 设置模板加载路径(指向存放 .ftl 文件的目录)
cfg.setDirectoryForTemplateLoading(new File("src/main/resources/templates"));
// 3. 准备数据模型
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("name", "李四");
dataModel.put("role", "管理员");
// 4. 加载模板
Template template = cfg.getTemplate("welcome.ftl");
// 5. 生成输出到文件
try (FileWriter out = new FileWriter("welcome.html")) {
template.process(dataModel, out);
System.out.println("welcome.html 文件已成功生成!");
}
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
}
运行程序
运行 FreemarkerDemo,你会在项目根目录下发现生成的 welcome.html 文件,内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">欢迎页</title>
</head>
<body>
<h2>欢迎, 李四!</h2>
<p>您的角色是: 管理员</p>
</body>
</html>
Freemarker 常用语法详解
1 注释
<#-- 这是一个注释,不会被输出到最终结果中 -->
2 输出变量:
这是最常用的语法,用于输出变量的值。
<p>用户名: ${username}</p>
如果变量不存在,会抛出异常,为了避免异常,可以使用空值处理(见 5.9)。
3 逻辑指令
<#if> 条件判断
<#if user.isLogged>
<p>欢迎回来,${user.name}!</p>
<#else>
<p>请先登录。</p>
</#if>
<#if user.age >= 18>
<p>您已成年。</p>
<#elseif user.age >= 13>
<p>您是青少年。</p>
<#else>
<p>您是儿童。</p>
</#if>
<#list> 循环遍历
<h3>商品列表:</h3>
<ul>
<#list products as product>
<li>${product_index + 1}. ${product.name} - 价格: ${product.price}</li>
</#list>
</ul>
在循环中,有两个特殊变量:
product_index: 当前项的索引(从 0 开始)。product_has_next: 判断是否还有下一项。
4 循环控制
<#break>: 立即跳出当前循环。<#continue>: 跳过当前迭代,继续下一次循环。
5 模板包含:<#include>, <#import>
<#include>
用于包含其他模板文件的内容,被包含的模板会直接共享当前模板的数据模型。
<#include "header.ftl"> <h1>页面主体</h1> <#include "footer.ftl">
<#import>
类似于 Java 的 import,用于导入一个库文件(通常是包含宏的模板),并为其创建一个命名空间。
<#import "/lib/common.ftl" as common> <!-- 导入 common.ftl,并命名为 common --> <#-- 使用 common 命名空间中的宏 --> <@common.greet "World"/>
6 宏定义:<#macro>, <@macro>
宏类似于函数或方法,可以接收参数,并在模板中重复使用。
定义宏 (在 my_lib.ftl 中):
<#macro greet person>
<p>你好, ${person}!</p>
</#macro>
<#macro repeat count, word>
<#list 1..count as i>
${word}<#if i != count>, </#if>
</#list>
</#macro>
使用宏:
<#import "my_lib.ftl" as my> <@my.greet person="宏的世界"/> <@my.repeat count=5, word="FreeMarker"/>
输出:
<p>你好, 宏的世界!</p> FreeMarker, FreeMarker, FreeMarker, FreeMarker, FreeMarker
7 命名空间
通过 <#import> 导入的模板会创建一个命名空间,可以有效避免宏名冲突。<@common.greet> 中的 common 就是命名空间。
8 内建函数
在变量后通过 调用,用于处理或转换数据。
?string: 将值转为字符串。${.now?string} <!-- 2025-10-27 10:30:00 --> ${.now?string("yyyy-MM-dd")} <!-- 2025-10-27 -->?html: 对字符串进行 HTML 转义,防止 XSS 攻击。${userInput?html} <!-- userInput 是 "<script>alert(1)</script>",会输出 <script>alert(1)</script> -->?cap_first: 首字母大写。?upper_case: 全部转为大写。?lower_case: 全部转为小写。?size: 获取集合或字符串的大小。<p>商品数量: ${products?size}</p>?date,?time,?datetime: 专门处理日期对象。
9 空值处理
这是 Freemarker 中非常重要的一个特性,用于防止因数据缺失导致的页面错误。
- 操作符: 当值为空时,提供一个默认值。
${user.name!"访客"} <!-- user 或 user.name 为空,则显示 "访客" --> - 操作符: 判断值是否存在,返回布尔值
true或false。<#if user??> <p>欢迎您,${user.name}</p> <#else> <p>您还未登录。</p> </#if>
10 算术和比较运算
- 算术: , , , , ,注意:数字和数字之间可以直接运算。
${price * 1.1} <!-- 计算含税价 --> - 比较: , ,
>,>=,<,<=,注意: 和 可以用于任何类型的值,而其他比较符只能用于数字和日期。<#if user.age >= 18> ... </#if>
最佳实践与注意事项
- 不要在模板中写复杂逻辑:模板应专注于展示,复杂的业务逻辑(如数据计算、过滤)应在 Java 端完成,然后将处理好的结果传递给模板。
- 始终使用 和 :养成良好习惯,对可能为空的变量进行处理,避免页面报错。
- 使用内建函数进行转义:对于任何用户输入的内容,在输出到 HTML 时,务必使用
?html进行转义,以防止 XSS 安全漏洞。 - 宏命名空间:在大型项目中,使用
<#import>和命名空间来组织宏,避免命名冲突。 - 模板缓存:
Configuration默认会缓存已加载的模板,这是性能优化的关键,不要频繁地创建和销毁Configuration实例,通常一个应用只需要一个全局的Configuration。 - 清晰的模板结构:合理使用
<#include>将页面拆分成头部、主体、底部等模块,提高可维护性。
实际应用场景
1 生成静态 HTML 页面
这是 Freemarker 的经典用途,电商网站的商品详情页、博客文章页等,内容不常变化,可以提前生成静态 HTML 文件,减轻服务器压力。
- 流程:后台任务定时触发,或内容发布时触发,将数据(商品信息、文章内容)和模板结合,生成
.html文件,并部署到 CDN 或静态服务器。
2 生成 XML/JSON 配置文件
在需要动态生成配置文件的场景中,Freemarker 非常有用。
- 示例:根据不同环境(开发、测试、生产)的配置参数,生成对应的
application-dev.xml或config-prod.json文件。
3 在 Spring Boot 中使用 Freemarker
Spring Boot 对 Freemarker 提供了极佳的支持,集成非常简单。
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
配置 application.yml
spring:
freemarker:
template-loader-path: classpath:/templates/ # 模板路径
suffix: .ftl # 模板后缀
charset: UTF-8
cache: true # 开启模板缓存
settings:
number_format: '0.##' # 数字格式化
创建 Controller
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Spring Boot + Freemarker");
model.addAttribute("time", new Date());
// 视图解析器会自动寻找 /templates/hello.ftl
return "hello";
}
}
创建模板 templates/hello.ftl
<!DOCTYPE html>
<html>
<head>Hello Page</title>
</head>
<body>
<h1>${message}</h1>
<p>当前时间: ${time?string('yyyy-MM-dd HH:mm:ss')}</p>
</body>
</html>
启动 Spring Boot 应用,访问 /hello 即可看到渲染后的页面。
总结与资源
Freemarker 是一个成熟、稳定且功能强大的模板引擎,特别适合于需要将动态数据与静态模板分离的场景,通过本教程,你应该已经掌握了其核心概念和常用语法,并能够将其应用到实际项目中。
官方资源
学习建议
- 动手实践:跟着教程敲一遍代码,然后尝试修改模板和数据,观察输出结果的变化。
- 多看官方文档:遇到问题时,第一选择是查阅官方文档,因为它最准确。
- 模仿优秀项目:如果有机会,可以看看一些开源项目中是如何使用 Freemarker 的,学习其最佳实践。
