📚 一、核心概念

1.1 FreeMarker 模板文件(.ftl)

FreeMarker 模板是 .ftl 文件,它由 静态文本(HTML、XML、文本等)和指令(FTL 标签) 组成。模板通过 数据模型(Data Model) 渲染后生成最终输出。

1.2 模板结构的核心组成部分

组成部分 描述
静态文本 HTML、XML、纯文本等,直接输出
插值(Interpolation) ${variable},用于插入变量值
指令(Directives) <#if>, <#list>, <#macro> 等控制逻辑
注释 <#-- 注释内容 -->
宏(Macro) 类似函数,可复用模板代码
引入与导入 <#include><#import> 拆分模板
全局变量 <#assign> 定义局部变量
输出格式控制 <#ftl output_format="HTML">

🛠️ 二、模板文件结构详解(含操作步骤)

2.1 基本模板结构(HTML 页面示例)

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>${title}</title>
</head>
<body>
    <h1>${title}</h1>
    <p>欢迎 ${user.name}!</p>

    <#if user.isAdmin>
        <p>您是管理员。</p>
    <#else>
        <p>您是普通用户。</p>
    </#if>

    <#list products as product>
        <div class="product">
            <h3>${product.name}</h3>
            <p>价格:${product.price}</p>
        </div>
    </#list>
</body>
</html>

2.2 模板拆分结构(推荐做法)

2.2.1 主模板:main.ftl

<#import "macros.ftl" as macros>

<#assign title = "首页">
<#include "header.ftl">

<h2>欢迎来到首页</h2>
<#list products as product>
    <@macros.product product=product />
</#list>

<#include "footer.ftl">

2.2.2 页头模板:header.ftl

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>${title}</title>
</head>
<body>

2.2.3 页脚模板:footer.ftl

</body>
</html>

2.2.4 宏模板:macros.ftl

<#macro product product>
    <div class="product">
        <h3>${product.name}</h3>
        <p>价格:${product.price}</p>
    </div>
</#macro>

2.3 数据模型(Java 示例)

Map<String, Object> dataModel = new HashMap<>();
dataModel.put("title", "产品列表");

Map<String, Object> user = new HashMap<>();
user.put("name", "张三");
user.put("isAdmin", true);
dataModel.put("user", user);

List<Map<String, Object>> products = new ArrayList<>();

Map<String, Object> product1 = new HashMap<>();
product1.put("name", "手机");
product1.put("price", "2999元");

Map<String, Object> product2 = new HashMap<>();
product2.put("name", "平板");
product2.put("price", "1999元");

products.add(product1);
products.add(product2);

dataModel.put("products", products);

⚠️ 三、常见错误与解决方案

错误 原因 解决方法
TemplateNotFoundException 找不到模板文件 检查路径是否正确
MalformedTemplateNameException 模板名非法 不含非法字符
InvalidFormatException FTL 语法错误 检查 <#if> 是否闭合等
TemplateModelException 数据类型不匹配 检查是否 List、Map 等
NullPointerException 变量未定义 使用 ${var!"default"}??
Unexpected token 模板语法错误 检查 ${<# 是否闭合

📝 四、注意事项

项目 说明
模板路径 推荐使用 classpath 或相对路径
模板缓存 默认开启,开发时可设为 cfg.setTemplateUpdateDelay(0)
编码问题 始终设置 setDefaultEncoding("UTF-8")
模板嵌套 控制嵌套层级,避免复杂结构
宏命名 使用有意义的命名,如 <@macros.header />
避免逻辑复杂化 模板只负责展示,不处理业务逻辑
使用 ?dump 调试 <#if data?dump> 可查看数据结构

🧪 五、使用技巧

技巧 示例
使用宏复用代码 <#macro> 定义,<@macro> 调用
使用 <#include> 拆分模板 页头、页脚、侧边栏等
使用 <#import> 引入宏库 集中管理宏
使用 ?exists 判断变量是否存在 <#if name?exists>
使用 ! 提供默认值 ${name!"匿名"}
使用 ?has_content 判断集合 <#if list?has_content>
使用 <#-- 注释 --> 模板注释,不输出到 HTML
使用 <#ftl> 设置格式 <#ftl output_format="HTML">
使用 <#assign> 定义局部变量 <#assign i = 0>

🚀 六、最佳实践与性能优化

6.1 最佳实践

实践 说明
模板结构清晰 分为 header、main、footer、macros
使用宏封装复用代码 提高可维护性
避免模板中做复杂逻辑 如判断、计算等应放在 Java 层
使用 <#include> 分离模板结构 提高可读性
使用 <#import> 管理宏库 避免重复定义
使用 ?has_content 替代 ?size == 0 更高效
使用 ?dump 调试模板数据 查看变量结构
使用 UTF-8 编码 避免乱码
使用 <#-- 注释 --> 不暴露注释到 HTML

6.2 性能优化

优化点 说明
模板缓存机制 FreeMarker 默认缓存模板,提高性能
避免频繁重新加载模板 生产环境设置 setTemplateUpdateDelay(-1)
减少 <#if> 嵌套层级 复杂逻辑提前在 Java 层处理
使用 ?has_content 替代 ?size 更高效
合并多个 <#include> 减少 I/O 次数
使用宏代替重复代码 减少冗余代码
使用 <#ftl output_format="HTML"> 提高渲染效率

📚 七、学习建议

阶段 学习建议
入门 熟悉 FTL 语法、变量、插值、条件、循环
进阶 学习宏、引入、导入、模板继承
高级 学习国际化、自定义指令、性能调优
实战 结合 Spring Boot、Web 项目、静态化生成
文档 FreeMarker 官方文档
示例 GitHub 示例项目
社区 FreeMarker 官方论坛

📌 八、总结

模块 内容
核心结构 静态文本、插值、指令、宏、引入
模板拆分 header、main、footer、macros
数据模型 Map、List、Java Bean
常见错误 模板找不到、变量未定义、语法错误
注意事项 编码、缓存、安全、结构清晰
使用技巧 宏、include、import、插值、默认值
最佳实践 分离结构、封装宏、避免复杂逻辑
性能优化 模板缓存、减少 I/O、避免嵌套