📚 一、核心概念
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">
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>${title}</title>
</head>
<body>
</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"> |
提高渲染效率 |
📚 七、学习建议
📌 八、总结
模块 |
内容 |
核心结构 |
静态文本、插值、指令、宏、引入 |
模板拆分 |
header、main、footer、macros |
数据模型 |
Map、List、Java Bean |
常见错误 |
模板找不到、变量未定义、语法错误 |
注意事项 |
编码、缓存、安全、结构清晰 |
使用技巧 |
宏、include、import、插值、默认值 |
最佳实践 |
分离结构、封装宏、避免复杂逻辑 |
性能优化 |
模板缓存、减少 I/O、避免嵌套 |