简介:Freemarker是一个广泛应用于Java Web开发的模板引擎,能够有效分离业务逻辑与视图层,提升代码可维护性。本文档系统讲解Freemarker的基础语法、高级功能及与Jeecms结合的模板开发技巧。通过学习,开发者可以掌握变量表达式、指令使用、模板继承、自定义标签与函数等核心技能,并熟练运用Jeecms标签实现网站布局、数据展示、分页处理等功能。配套学习资源帮助从入门到实战全面提升模板开发能力。
1. Freemarker模板引擎概述
Freemarker 是一款基于 Java 的模板引擎,专为实现动态数据与静态页面的高效融合而设计。它通过模板文件(.ftl)将业务逻辑与表现层分离,广泛应用于 Web 开发,特别是在内容管理系统(如 Jeecms)中发挥着核心作用。
其核心机制是:通过数据模型(Model)与模板(View)的结合,在运行时生成最终的 HTML 页面。这种机制不仅提升了开发效率,也增强了系统的可维护性与可扩展性。在 Jeecms 中,Freemarker 被用于构建网站前台页面,实现内容的动态渲染和模板复用。
掌握 Freemarker 的使用,对于构建高性能、易维护的 Web 应用具有重要意义。
2. Freemarker基础语法详解
Freemarker 作为一种基于 Java 的模板引擎,其语法设计简洁且强大,尤其适合用于将动态数据嵌入 HTML 页面中。理解其基础语法是掌握 Freemarker 的关键。本章将从模板文件的结构、表达式的基本构成,到常用指令的使用方式,逐步深入地介绍 Freemarker 的语法体系,帮助开发者构建出结构清晰、逻辑严谨的模板文件。
2.1 模板文件的结构与格式
Freemarker 模板文件通常以 .ftl (Freemarker Template Language)为扩展名,其本质是一个文本文件,可以是 HTML、XML、纯文本等形式。模板中嵌入了动态数据的表达式和控制结构,通过 Freemarker 引擎处理后,生成最终的 HTML 页面或其他格式的内容。
2.1.1 HTML 与 FTL 文件的融合方式
在 Web 开发中,Freemarker 模板最常用于与 HTML 结合。HTML 是静态结构,而 FTL 是动态逻辑的载体。两者融合的方式如下:
- HTML 作为静态结构 :页面的整体布局、样式、脚本等静态内容由 HTML 编写。
- FTL 提供动态逻辑 :变量插值、条件判断、循环结构、宏定义等由 FTL 表达式实现。
示例:HTML 与 FTL 融合模板
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>${siteName}</title>
</head>
<body>
<h1>欢迎访问 ${siteName}</h1>
<ul>
<#list menus as menu>
<li><a href="${menu.url}">${menu.name}</a></li>
</#list>
</ul>
</body>
</html>
逻辑分析与参数说明:
-
${siteName}:这是一个插值表达式,表示将变量siteName的值插入到 HTML 中。 -
<#list menus as menu>:这是 Freemarker 的循环指令,遍历menus集合中的每一个元素,并命名为menu。 -
menu.url和menu.name:分别表示当前menu对象的属性值。
这种融合方式使得前端结构清晰,后端逻辑可控,是现代 Web 开发中非常常见的一种做法。
2.1.2 常见的文件扩展名与编码规范
| 文件类型 | 扩展名 | 说明 |
|---|---|---|
| HTML 模板 | .ftl 或 .html | 推荐使用 .ftl 明确模板语言类型 |
| 纯文本模板 | .ftl | 用于生成邮件、配置文件等文本内容 |
| XML 模板 | .ftl | 用于生成 XML 格式的数据结构 |
编码规范建议:
- 使用 UTF-8 编码以支持多语言字符。
- 模板文件命名应具有语义性,如
index.ftl、header.ftl。 - 模板目录结构应清晰,建议按功能模块划分目录,如
/templates/article/、/templates/user/。
2.2 表达式的基本构成
Freemarker 的表达式是模板中动态数据处理的核心,包括文本、变量、插值、运算等。
2.2.1 文本、变量与插值的使用
在 Freemarker 中,文本可以直接输出,而变量和插值则用于动态内容的插入。
文本直接输出:
欢迎使用 Freemarker 模板引擎。
变量定义与引用:
<#assign username = "张三">
<p>当前用户:${username}</p>
插值表达式:
-
${variable}:将变量的值插入到文本中。 -
#{number}:用于数字格式化输出。 -
@{resource}:用于资源定位(如国际化消息)。
示例代码:
<#assign age = 25>
<#assign city = "北京">
<p>姓名:${username},年龄:${age},城市:${city}</p>
逻辑分析与参数说明:
-
<#assign>:用于定义变量,作用域为当前模板。 -
${username}:插值表达式,输出变量值。 - 支持字符串、数字、布尔、集合等多种数据类型。
2.2.2 简单运算与类型转换
Freemarker 支持常见的算术、逻辑和比较运算。
算术运算:
<#assign a = 10 + 5>
<#assign b = a * 2>
<p>结果为:${b}</p>
类型转换:
<#assign numStr = "123">
<#assign num = numStr?number>
<p>转换后的数字为:${num + 1}</p>
比较与逻辑运算:
<#if age > 18 && age < 30>
<p>年龄在 18 到 30 之间</p>
<#else>
<p>年龄不在 18 到 30 之间</p>
</#if>
逻辑分析与参数说明:
-
?number:Freemarker 内置的类型转换函数,将字符串转换为数字。 -
&&、||、!:逻辑运算符,用于构建复杂判断条件。 -
>,<,==:比较运算符,用于判断变量值的大小或相等性。
2.3 指令的使用基础
Freemarker 提供了多种控制结构指令,用于实现条件判断、循环遍历、变量定义等功能。
2.3.1 list、if 等常见指令的语法结构
<#list> 指令
用于遍历集合或数组:
<#assign colors = ["红", "绿", "蓝"]>
<ul>
<#list colors as color>
<li>${color}</li>
</#list>
</ul>
<#if> 指令
用于条件判断:
<#if user.isAdmin>
<p>欢迎管理员</p>
<#else>
<p>欢迎普通用户</p>
</#if>
<#else if> 指令
支持多条件判断:
<#if score >= 90>
<p>优秀</p>
<#else if score >= 80>
<p>良好</p>
<#else>
<p>及格</p>
</#if>
流程图展示(mermaid 格式):
graph TD
A[开始] --> B{条件判断}
B -->|条件成立| C[执行 if 分支]
B -->|条件不成立| D[执行 else 分支]
C --> E[结束]
D --> E
2.3.2 指令嵌套与代码块的控制方式
Freemarker 支持指令的嵌套使用,以实现更复杂的逻辑控制。
示例:嵌套 <#list> 与 <#if>
<#assign users = [
{"name": "Alice", "isAdmin": true},
{"name": "Bob", "isAdmin": false},
{"name": "Charlie", "isAdmin": true}
]>
<ul>
<#list users as user>
<#if user.isAdmin>
<li style="color:red;">管理员:${user.name}</li>
<#else>
<li>普通用户:${user.name}</li>
</#else>
</#list>
</ul>
逻辑分析与参数说明:
-
users是一个包含多个用户的集合。 -
<#list>遍历每个用户。 -
<#if>判断用户是否为管理员,并应用不同的样式输出。
表格:常见指令及其用途
| 指令 | 用途说明 |
|---|---|
<#list> | 遍历集合或数组 |
<#if> | 条件判断 |
<#else> | 否则分支 |
<#else if> | 多条件判断 |
<#assign> | 定义变量 |
<#macro> | 定义宏(可复用模板片段) |
通过本章的学习,开发者应能熟练掌握 Freemarker 模板的基本结构、表达式使用方式以及常见指令的语法与逻辑。这些内容是构建复杂模板的基础,为后续章节中更高级的控制结构和模板复用机制打下坚实基础。
3. 变量表达式与条件判断
在Freemarker模板开发中, 变量表达式与条件判断 构成了模板逻辑控制的核心机制。通过灵活使用变量的定义与引用,以及条件语句的结构控制,开发者可以构建出高度动态、交互性强的网页内容。本章将深入探讨Freemarker中变量的定义方式、作用域规则,以及如何利用条件判断语句(如 if 、 else if 、 else )来实现逻辑分支控制。此外,我们还将通过实际案例演示变量与条件语句的结合应用,并分析在页面渲染过程中如何优化条件判断的性能与可读性。
3.1 变量的定义与引用
在Freemarker中,变量是存储和传递数据的关键载体。开发者可以通过模板指令来定义变量,并在后续的表达式或逻辑控制中引用这些变量。
3.1.1 全局变量与局部变量的作用域
Freemarker支持两种变量作用域:全局变量和局部变量。
- 全局变量 :在整个模板中都可访问的变量,通常通过
<#assign>指令在模板顶层定义。 - 局部变量 :仅在某个指令块(如
if、list等)内部有效,通常用于控制逻辑流程。
示例代码:
<#assign globalVar = "I'm global">
<#list ["a", "b", "c"] as item>
<#assign localVar = "I'm local to this loop">
${globalVar} - ${localVar}
</#list>
逻辑分析:
-
<#assign globalVar = "I'm global">:定义了一个全局变量globalVar,其值在整个模板中都可访问。 -
<#list ... as item>:开始一个循环,遍历数组["a", "b", "c"]。 -
<#assign localVar = "I'm local to this loop">:在每次循环中定义一个局部变量localVar,该变量只在当前循环块内有效。 -
${globalVar} - ${localVar}:输出全局变量与局部变量的值。
⚠️ 注意:如果在循环外部尝试访问
localVar,Freemarker 会抛出异常,因为该变量超出其作用域。
变量作用域表格对比:
| 变量类型 | 定义位置 | 作用域范围 | 生命周期 |
|---|---|---|---|
| 全局变量 | 模板顶层 | 整个模板 | 整个模板渲染期间 |
| 局部变量 | 某个指令块内部 | 当前指令块 | 当前指令块执行期间 |
3.1.2 变量的赋值与更新方式
在Freemarker中,变量可以通过 <#assign> 指令进行赋值或更新。虽然Freemarker本质上是模板引擎,变量更新的机制不同于传统编程语言中的可变变量,但通过合理使用 <#assign> ,可以在一定程度上模拟“变量更新”的效果。
示例代码:
<#assign count = 0>
<#list 1..5 as i>
<#assign count = count + 1>
当前计数器:${count}
</#list>
逻辑分析:
-
<#assign count = 0>:初始化计数器变量count为 0。 -
<#list 1..5 as i>:遍历数字 1 到 5。 -
<#assign count = count + 1>:每次循环中将count值加 1。 -
${count}:输出当前的count值。
执行结果:
当前计数器:1
当前计数器:2
当前计数器:3
当前计数器:4
当前计数器:5
✅ 提示:虽然Freemarker的变量不支持传统意义上的“可变性”,但
<#assign>指令允许在相同作用域中重新赋值,从而实现变量值的“更新”。
3.2 条件语句的使用
条件判断是控制模板输出逻辑的重要手段。Freemarker 提供了 if 、 else if 和 else 等条件语句,使得开发者可以根据不同的数据状态渲染不同的内容。
3.2.1 if、else if、else语句的结构
Freemarker 的条件语句结构与大多数编程语言类似,支持多分支判断。
示例代码:
<#assign score = 85>
<#if score >= 90>
成绩优秀!
<#elseif score >= 70>
成绩良好!
<#else>
成绩需努力!
</#if>
逻辑分析:
-
<#assign score = 85>:定义一个变量score,值为 85。 -
<#if score >= 90>:判断成绩是否大于等于 90,若为真,输出“成绩优秀!”。 -
<#elseif score >= 70>:如果第一个条件为假,继续判断是否大于等于 70,若为真,输出“成绩良好!”。 -
<#else>:所有条件都为假时,输出“成绩需努力!”。
执行结果:
成绩良好!
3.2.2 多条件判断与逻辑运算符的运用
在实际开发中,经常需要对多个条件进行组合判断。Freemarker 支持使用逻辑运算符 && (与)、 || (或)、 ! (非)来进行复杂条件判断。
示例代码:
<#assign age = 25>
<#assign gender = "female">
<#if (age >= 18 && age <= 30) || gender == "female">
满足条件,可以参与活动!
<#else>
不符合条件,无法参与。
</#if>
逻辑分析:
-
<#assign age = 25>:定义年龄变量。 -
<#assign gender = "female">:定义性别变量。 -
<#if (age >= 18 && age <= 30) || gender == "female">:
- 判断年龄是否在 18~30 之间 并且 性别是否为女性;
- 或者只要性别是女性就满足条件。
执行结果:
满足条件,可以参与活动!
✅ 建议:在多条件判断中使用括号明确逻辑优先级,避免歧义。
逻辑判断流程图(mermaid格式):
graph TD
A[开始判断] --> B{年龄是否在18~30之间}
B -->|是| C{性别是否为女性}
B -->|否| D{性别是否为女性}
C -->|是| E[满足条件]
C -->|否| F[不满足条件]
D -->|是| G[满足条件]
D -->|否| H[不满足条件]
3.3 变量表达式的综合应用
在实际项目中,变量表达式与条件语句往往结合使用,以实现更复杂的页面逻辑控制。例如根据用户角色显示不同内容、根据状态显示不同按钮等。
3.3.1 变量与条件结合的实战案例
场景:根据用户角色显示不同内容
<#assign user = {
"name": "张三",
"role": "admin"
}>
<#if user.role == "admin">
<p>欢迎管理员 ${user.name},您拥有全部权限。</p>
<#elseif user.role == "editor">
<p>欢迎编辑者 ${user.name},您可以编辑内容。</p>
<#else>
<p>欢迎普通用户 ${user.name},您只能查看内容。</p>
</#if>
逻辑分析:
- 定义用户对象 :包含用户名和角色。
- 根据角色输出不同欢迎语 :
- 如果是admin,显示管理员欢迎语;
- 如果是editor,显示编辑者欢迎语;
- 否则显示普通用户欢迎语。
执行结果:
欢迎管理员 张三,您拥有全部权限。
3.3.2 条件判断在页面渲染中的优化策略
在实际开发中,频繁使用条件判断可能会影响模板的性能和可读性。以下是几种优化策略:
1. 使用 <#switch> 替代多个 <#elseif> (Freemarker 2.3.29+ 支持)
<#assign role = "admin">
<#switch role>
<#case "admin">
管理员权限
<#break>
<#case "editor">
编辑权限
<#break>
<#default>
普通权限
</#switch>
✅ 优点:代码结构更清晰,避免大量
if-else嵌套。
2. 避免重复条件判断
将常用判断结果提前赋值给变量,避免多次计算:
<#assign isAdmin = (user.role == "admin")>
<#if isAdmin>
管理员内容
</#if>
3. 使用模板片段封装重复逻辑
将重复的条件判断逻辑封装到宏或模板片段中:
<#macro renderRoleInfo role>
<#if role == "admin">
管理员权限
<#elseif role == "editor">
编辑权限
<#else>
普通权限
</#if>
</#macro>
${renderRoleInfo(user.role)}
优化策略对比表格:
| 优化方式 | 适用场景 | 优点 | 注意事项 |
|---|---|---|---|
<#switch> | 多分支判断 | 结构清晰 | Freemarker 2.3.29+ 才支持 |
| 提前赋值变量 | 重复判断 | 提高性能 | 仅适用于静态判断 |
| 使用宏封装 | 多次复用逻辑 | 提高可维护性 | 需合理命名与组织 |
通过合理使用变量表达式与条件判断,开发者可以构建出逻辑清晰、结构良好的模板页面。在下一章中,我们将深入探讨循环结构与注释语法,进一步增强模板的动态控制能力。
4. 模板循环与注释语法
循环结构是模板中处理列表数据的核心工具,而注释则是代码可维护性的重要保障。本章将深入探讨Freemarker中 list 指令的使用方式,包括基本循环结构、索引控制、条件判断嵌套,以及break与continue的替代实现。同时,我们将介绍注释的语法及其在模板开发中的实际应用价值。
4.1 list指令的使用
list 是Freemarker中最常用的指令之一,用于遍历集合或数组类型的数据。掌握其使用方式,是实现数据动态渲染的关键。
4.1.1 基本的循环结构与索引控制
Freemarker通过 <#list> 指令对集合进行迭代,其基本语法如下:
<#list collection as item>
${item}
</#list>
其中, collection 是待遍历的数据源, item 是当前迭代项的变量名。Freemarker还提供了 item_index 变量来获取当前项的索引,索引从0开始计数。
示例:展示文章列表并显示索引
<#list articles as article>
<p>第${article_index + 1}篇文章:${article.title}</p>
</#list>
代码解析:
-
<#list articles as article>:遍历名为articles的集合,每次循环中当前元素被赋值给article变量。 -
article_index:Freemarker内置的索引变量,默认从0开始。 -
${article_index + 1}:为了显示从1开始的序号。 -
${article.title}:显示文章标题字段。
表格:常见集合类型支持情况
| 集合类型 | 是否支持遍历 | 备注 |
|---|---|---|
| List | ✅ | Java中的List结构 |
| Map | ✅ | 可遍历键值对,使用 keys 或 entries |
| Set | ✅ | 无序遍历 |
| Array | ✅ | 基本类型数组也可遍历 |
| 数据库结果集 | ✅(需封装) | 需要转换为List或Map |
4.1.2 循环中嵌套条件判断与函数调用
在实际开发中,常常需要在循环中进行条件判断或调用函数,以实现更复杂的逻辑控制。
示例:根据文章状态显示不同样式
<#list articles as article>
<#if article.status == "published">
<div class="published">${article.title}</div>
<#else>
<div class="draft">${article.title}(草稿)</div>
</#if>
</#list>
代码解析:
-
<#if article.status == "published">:判断文章状态是否为“已发布”。 -
<div class="published">...</div>:若为发布状态,使用特定样式展示。 -
<div class="draft">...</div>:否则,展示为草稿状态。
示例:调用函数格式化日期
<#list articles as article>
<p>标题:${article.title},发布时间:<@formatDate date=article.publishDate format="yyyy-MM-dd"/></p>
</#list>
假设 formatDate 是一个自定义宏函数,用于格式化日期输出。
代码解析:
-
<@formatDate ...>:调用宏函数formatDate。 -
date=article.publishDate:传入日期参数。 -
format="yyyy-MM-dd":指定日期格式字符串。
mermaid流程图:list指令与条件判断嵌套流程
graph TD
A[开始遍历文章列表] --> B{判断文章状态}
B -- 已发布 --> C[展示为发布样式]
B -- 草稿 --> D[展示为草稿样式]
A --> E[遍历结束?]
E -- 否 --> A
E -- 是 --> F[循环结束]
4.2 高级循环控制
在复杂模板中,往往需要更精细地控制循环行为。Freemarker虽未直接提供 break 和 continue 关键字,但可通过逻辑判断实现类似功能。
4.2.1 break与continue的替代方案
虽然Freemarker不支持 break 和 continue ,但可以使用 <#break> 指令(仅在 <#list> 循环内部有效)来提前终止循环,或使用条件跳过部分迭代。
示例:只展示前5篇文章
<#list articles as article>
<#if article_index >= 5>
<#break>
</#if>
<p>${article.title}</p>
</#list>
代码解析:
-
<#if article_index >= 5>:判断当前索引是否大于等于5。 -
<#break>:如果满足条件,跳出循环,停止后续迭代。
示例:跳过状态为“下架”的文章
<#list articles as article>
<#if article.status == "offline">
<#continue>
</#if>
<p>${article.title}</p>
</#list>
代码解析:
-
<#if article.status == "offline">:判断文章是否为“下架”状态。 -
<#continue>:如果是,则跳过本次循环,不执行后续代码。
4.2.2 多层循环的嵌套处理技巧
在处理复杂数据结构(如嵌套列表、树形结构)时,常常需要进行多层循环嵌套。
示例:展示栏目下的文章分类及文章列表
<#list categories as category>
<h3>${category.name}</h3>
<ul>
<#list category.articles as article>
<li>${article.title}</li>
</#list>
</ul>
</#list>
代码解析:
- 外层循环遍历栏目列表
categories。 - 内层循环遍历每个栏目下的文章列表
category.articles。 - 展示每个栏目名称,并列出其下的文章标题。
表格:多层循环性能建议
| 场景 | 推荐方式 | 备注 |
|---|---|---|
| 小数据量嵌套结构 | 直接嵌套循环 | 简洁直观 |
| 大数据量(如1000+项) | 前端分页或懒加载 | 避免一次性渲染过多内容 |
| 动态数据变化频繁 | 使用缓存或模板预编译 | 提升渲染效率 |
| 非线性结构(如树形) | 使用递归宏函数或组件化结构 | 提高代码可维护性 |
mermaid流程图:多层循环嵌套执行流程
graph TD
A[开始外层循环] --> B{是否还有栏目?}
B -- 是 --> C[进入内层循环]
C --> D{是否有文章?}
D -- 是 --> E[展示文章]
D -- 否 --> F[跳过]
C --> G[内层循环结束]
B -- 否 --> H[循环结束]
4.3 注释的使用方式
在模板开发中,良好的注释习惯不仅能提高代码可读性,也有助于团队协作与后期维护。
4.3.1 单行注释与多行注释的语法
Freemarker支持两种注释方式:
- 单行注释:
<#-- 注释内容 --> - 多行注释:使用相同语法,可跨多行书写。
示例:单行注释说明字段含义
<#-- 显示文章标题 -->
<h2>${article.title}</h2>
示例:多行注释说明模块作用
<#--
以下代码用于展示文章摘要信息
包括作者、发布时间、阅读量等字段
-->
<div class="summary">
<p>作者:${article.author}</p>
<p>时间:${article.publishDate}</p>
<p>阅读数:${article.views}</p>
</div>
代码解析:
-
<#-- ... -->:注释块,Freemarker不会将其渲染到最终输出中。 - 适用于解释代码逻辑、说明功能模块、记录开发者备注等。
4.3.2 注释在模板调试与团队协作中的价值
在团队协作中,清晰的注释能显著提升代码可维护性。特别是在调试模板时,可以通过注释快速定位逻辑问题。
示例:临时注释掉代码进行调试
<#-- 以下代码为测试用,上线前应删除 -->
<#list testArticles as article>
<p>测试文章:${article.title}</p>
</#list>
表格:注释在不同场景下的作用
| 场景 | 注释作用 | 示例 |
|---|---|---|
| 代码说明 | 解释代码逻辑,便于理解 | <#-- 显示文章标题 --> |
| 模块说明 | 标记模块功能,便于维护 | <#-- 文章摘要信息 --> |
| 临时调试 | 快速禁用代码,不影响整体结构 | <#-- 测试代码 --> |
| 版本说明 | 记录修改信息,便于版本控制 | <#-- v1.0 新增阅读数字段 --> |
| 权限控制 | 注释掉部分用户不可见内容 | <#-- 管理员可见内容 --> |
| 避免重复开发 | 提示已有功能,防止重复编写 | <#-- 已有通用头部模板 --> |
mermaid流程图:注释在团队协作中的价值体现
graph TD
A[开发者A编写模板] --> B[添加详细注释]
B --> C[开发者B阅读模板]
C --> D{是否理解代码逻辑?}
D -- 是 --> E[快速上手开发]
D -- 否 --> F[沟通成本增加]
总结:
本章系统讲解了Freemarker中循环结构的使用方法,包括基础 list 指令、索引控制、条件判断嵌套、函数调用,以及break/continue的替代实现和多层循环处理技巧。同时,深入探讨了注释语法的使用方式及其在开发、调试和团队协作中的重要作用。通过本章内容,开发者将能够编写出结构清晰、逻辑严谨、可维护性强的模板代码。
5. Freemarker内置指令使用
Freemarker 提供了一系列强大的内置指令,帮助开发者高效地组织模板逻辑、封装复用代码、管理模块依赖等。掌握这些内置指令不仅能提升模板开发的效率,还能增强模板的可维护性与可扩展性。本章将深入探讨 Freemarker 中几个核心的内置指令,如 assign 、 macro 、 import 等,并分析它们之间的调用关系和使用场景。此外,还将重点讲解宏与函数的定义与调用方式,帮助读者掌握如何在实际项目中构建可复用的模板组件。
5.1 常用内置指令介绍
Freemarker 的内置指令是构建模板逻辑的基础工具,它们提供了变量赋值、宏定义、模块引入等功能。本节将详细介绍 assign 、 macro 、 import 等核心指令的使用方式及其在模板开发中的典型应用场景。
5.1.1 assign、macro、import等核心指令
assign 指令
assign 指令用于在模板中定义或修改变量。它可以定义一个局部变量或全局变量,并为其赋值。 assign 语法如下:
<#assign variableName = value>
例如:
<#assign username = "JohnDoe">
<p>当前用户:${username}</p>
逻辑分析:
- 第一行使用
<#assign>定义了一个名为username的变量,并赋值为"JohnDoe"。 - 第二行通过
${}插值表达式将变量值输出到页面上。
参数说明:
-
variableName:变量名称,命名应符合 Java 标识符命名规范。 -
value:变量的值,可以是字符串、数字、布尔值、列表、哈希表等。
macro 指令
macro 指令用于定义宏(类似于函数),可以重复调用,提高模板的复用性。其语法如下:
<#macro macroName param1 param2 ...>
... body ...
</#macro>
例如,定义一个用于显示用户信息的宏:
<#macro showUserInfo name age>
<p>姓名:${name},年龄:${age}</p>
</#macro>
<@showUserInfo name="Alice" age=25 />
逻辑分析:
- 定义了一个名为
showUserInfo的宏,接受两个参数name和age。 - 使用
<@...>语法调用该宏,并传入具体值。
参数说明:
-
macroName:宏的名称。 -
param1 param2 ...:宏的参数,可选。 - 宏体内可以包含任意 FTL 代码,包括变量、条件判断、循环等。
import 指令
import 指令用于导入其他模板文件中的宏和变量,实现模块化开发。其语法如下:
<#import "path/to/template.ftl" as namespace>
例如,假设有一个 utils.ftl 文件定义了多个通用宏:
<!-- utils.ftl -->
<#macro formatDate date>
${date?string("yyyy-MM-dd")}
</#macro>
在其他模板中可以这样导入并使用:
<#import "utils.ftl" as utils>
<#assign today = .now>
<p>今天日期是:${utils.formatDate(today)}</p>
逻辑分析:
- 第一行将
utils.ftl导入为utils命名空间。 - 第二行使用命名空间调用
formatDate宏,传入当前时间。
参数说明:
-
"path/to/template.ftl":被导入的模板路径,支持相对路径。 -
as namespace:为导入内容指定命名空间,防止命名冲突。
5.1.2 指令之间的依赖与调用关系
Freemarker 的内置指令之间存在一定的依赖和调用关系,合理使用这些关系可以构建结构清晰、逻辑分明的模板系统。
示例:变量与宏的组合调用
<#assign user = {"name": "Bob", "age": 30}>
<#macro displayUser user>
<p>用户名:${user.name},年龄:${user.age}</p>
</#macro>
<@displayUser user=user />
逻辑分析:
- 使用
assign定义一个包含用户信息的哈希表变量user。 - 定义宏
displayUser接收一个用户对象并输出信息。 - 调用宏时将
user变量作为参数传入。
示例:跨模板调用宏
<!-- templateA.ftl -->
<#macro greeting name>
<p>你好,${name}!欢迎来到我们的网站。</p>
</#macro>
<!-- templateB.ftl -->
<#import "templateA.ftl" as a>
<@a.greeting name="Tom" />
逻辑分析:
- 在
templateB.ftl中通过import引入templateA.ftl中的宏。 - 使用命名空间调用
greeting宏,实现跨模板逻辑复用。
指令调用关系图(mermaid 流程图)
graph TD
A[assign] --> B[定义变量]
C[macro] --> D[定义宏]
D --> E[调用宏]
E --> F{是否跨模板调用}
F -- 是 --> G[import]
F -- 否 --> H[直接使用]
G --> I[调用导入宏]
图示说明:
-
assign用于定义变量。 -
macro定义宏后可通过<@>调用。 - 若宏定义在其他模板中,需使用
import导入后再调用。
5.2 宏与函数的定义与调用
宏(macro)是 Freemarker 中用于封装重复逻辑的重要机制,而函数(function)则是宏的一种扩展形式,支持返回值处理。本节将深入讲解宏的定义方式、参数传递机制以及如何在实际项目中构建可复用的函数。
5.2.1 自定义宏的语法结构
宏的基本结构由 <#macro> 开始,以 </#macro> 结束,中间为宏体内容。宏可以接受参数,并在调用时传入具体值。
宏的定义与调用示例
<#macro renderArticle title content author>
<div class="article">
<h2>${title}</h2>
<p>${content}</p>
<p>作者:${author}</p>
</div>
</#macro>
<@renderArticle title="Freemarker入门指南" content="本教程介绍Freemarker的基本使用方法。" author="张三" />
逻辑分析:
- 定义宏
renderArticle,接收三个参数:title、content、author。 - 宏体内构建一个 HTML 结构的
div,包含标题、内容和作者信息。 - 调用宏时传入具体参数值,输出渲染后的 HTML。
宏的默认参数与可选参数
宏支持为参数设置默认值,调用时可选择性传入:
<#macro greet name="Guest">
<p>你好,${name}!</p>
</#macro>
<@greet name="Tom" /> <!-- 输出:你好,Tom! -->
<@greet /> <!-- 输出:你好,Guest! -->
参数说明:
-
name="Guest":为name参数设置默认值。 - 若调用时不传入参数,则使用默认值。
5.2.2 函数参数的传递与返回值处理
虽然 Freemarker 原生不支持“函数”类型,但可以通过宏模拟函数行为,并通过 return 实现返回值机制(需结合 Java API 实现)。
使用宏模拟函数行为
<#macro calculateSum a b>
<#assign result = a + b>
<p>计算结果为:${result}</p>
</#macro>
<@calculateSum a=5 b=3 /> <!-- 输出:计算结果为:8 -->
逻辑分析:
- 宏
calculateSum接收两个参数a和b。 - 使用
assign将它们相加后赋值给result。 - 输出计算结果。
模拟返回值(结合 Java API)
在 Java 代码中定义一个方法并注册为 Freemarker 的自定义函数:
public class MathUtil {
public static int multiply(int a, int b) {
return a * b;
}
}
注册到 Freemarker 配置中:
configuration.setSharedVariable("math", new MathUtil());
在模板中调用:
${math.multiply(4, 5)} <!-- 输出:20 -->
逻辑分析:
- Java 类
MathUtil中定义了一个静态方法multiply。 - 通过
setSharedVariable将其实例注册为模板中的变量math。 - 模板中通过
${}调用方法并传参,返回计算结果。
5.2.3 宏与函数的复用策略
在大型项目中,宏和函数的复用性是提升开发效率的关键。可以通过以下方式构建可复用组件:
- 提取公共宏 :将常用 UI 组件封装为宏,如按钮、表单、卡片等。
- 集中管理宏文件 :建立
macros/文件夹,按功能分类存放宏文件,便于维护。 - 命名空间化调用 :使用
import引入宏并指定命名空间,避免命名冲突。 - 动态参数传递 :宏支持传入任意类型参数,适用于构建灵活组件。
示例:构建通用按钮宏
<!-- macros/button.ftl -->
<#macro button text link class="">
<a href="${link}" class="btn ${class}">${text}</a>
</#macro>
调用方式:
<#import "macros/button.ftl" as btn>
<@btn.button text="点击我" link="/home" class="primary" />
逻辑分析:
- 定义按钮宏,支持传入文字、链接、样式类名。
- 调用时通过命名空间
btn访问宏,传入参数生成 HTML 链接。
5.2.4 宏调用流程图(mermaid)
graph TD
A[定义宏] --> B[声明参数]
B --> C[编写宏体]
D[调用宏] --> E[传入参数]
E --> F[执行宏体]
F --> G[输出结果]
H[导入宏] --> I[指定命名空间]
I --> J[调用导入宏]
图示说明:
- 宏的生命周期从定义到调用,涉及参数声明、宏体编写、参数传入、执行与输出。
- 若宏定义在其他文件中,需先导入并指定命名空间,再进行调用。
5.2.5 宏与函数的性能优化建议
- 避免重复宏调用 :在循环或频繁渲染区域尽量减少宏调用次数,或缓存宏输出。
- 合理使用命名空间 :避免命名冲突,提高代码可读性。
- 宏参数优化 :控制参数数量,避免过多参数影响可维护性。
- 模板预编译 :在应用启动阶段预编译常用宏模板,提升运行效率。
通过本章的深入解析,读者可以全面掌握 Freemarker 的核心内置指令及其组合使用方式,理解宏与函数的定义与调用机制,并能够在实际项目中灵活运用这些功能,构建高效、可维护的模板系统。下一章将继续探讨模板的继承与复用机制,进一步提升模板工程化能力。
6. 模板继承与复用机制
模板继承与复用是 Freemarker 实现模块化开发、提升模板可维护性和扩展性的关键机制。通过继承机制,可以实现页面结构的统一,避免重复代码的编写;而模板复用则允许开发者将公共部分提取为独立组件,提升开发效率和代码的可读性。本章将从模板继承的基本概念出发,深入讲解 block 与 extends 指令的使用方法,并进一步探讨多层继承、模板覆盖、组件化模板构建等高级技巧。
6.1 模板继承的基本概念
模板继承(Template Inheritance)是一种允许子模板继承父模板结构,并在其中定义或覆盖特定部分的技术。它模仿了面向对象编程中的继承机制,使得模板设计更加模块化和灵活。
6.1.1 父模板与子模板的定义方式
在 Freemarker 中,模板继承通过 extends 指令和 block 指令配合实现。
- 父模板(Parent Template) :定义基础结构,包含多个
block占位符,用于被子模板覆盖。 - 子模板(Child Template) :使用
extends指令继承父模板,并通过block指令定义或重写内容。
示例代码:父模板 layout.ftl
<!DOCTYPE html>
<html>
<head>
<title><@block name="title">默认标题</@block></title>
</head>
<body>
<header>
<@block name="header">
<h1>网站头部</h1>
</@block>
</header>
<main>
<@block name="content">
<p>默认内容</p>
</@block>
</main>
<footer>
<@block name="footer">
<p>网站底部</p>
</@block>
</footer>
</body>
</html>
示例代码:子模板 home.ftl
<#extends "layout.ftl">
<@block name="title">首页</@block>
<@block name="header">
<h1>欢迎访问首页</h1>
</@block>
<@block name="content">
<p>这是首页的主内容。</p>
</@block>
代码逻辑分析:
-
<#extends>:指定当前模板继承自layout.ftl。 -
<@block>:定义或覆盖父模板中定义的block区域。 - 如果子模板没有定义某个
block,Freemarker 会使用父模板中的默认内容。
6.1.2 block 与 extends 指令的使用方法
extends 指令
语法:
<#extends template_name>
作用:指示当前模板继承自指定的父模板。该指令必须放在模板的最顶部。
block 指令
语法:
<@block name="block_name">
...内容...
</@block>
作用:定义或覆盖模板中的某个区域。子模板可以覆盖父模板中定义的 block,也可以使用 <@super> 调用父类内容。
示例:使用 <@super> 调用父模板内容
<#extends "layout.ftl">
<@block name="footer">
<@super/> <!-- 调用父模板的 footer 内容 -->
<p>新增的底部信息</p>
</@block>
参数说明:
| 参数 | 说明 |
|---|---|
template_name | 要继承的父模板名称,通常是相对于模板路径的字符串 |
name | block 的名称,必须与父模板中的 block 名一致 |
<@super> | 调用父模板中对应 block 的原始内容 |
6.2 模板复用的高级技巧
在实际项目中,仅使用模板继承往往不能满足复杂的组件化开发需求。Freemarker 还支持模板的嵌套复用、多层继承以及组件化构建,这些技巧可以显著提升模板的可复用性与开发效率。
6.2.1 多层继承与模板覆盖机制
多层继承是指一个模板可以继承自另一个子模板,形成继承链。这种机制在构建大型网站时非常有用,例如:
- 父模板(
base.ftl):定义网站基础结构 - 子模板(
layout.ftl):继承base.ftl并添加栏目布局 - 页面模板(
home.ftl):继承layout.ftl并定义具体页面内容
示例结构:
base.ftl
└── layout.ftl
└── home.ftl
示例代码: base.ftl
<html>
<head>
<title><@block name="title">默认标题</@block></title>
</head>
<body>
<@block name="content"></@block>
</body>
</html>
示例代码: layout.ftl
<#extends "base.ftl">
<@block name="content">
<div class="container">
<@block name="main"></@block>
</div>
</@block>
示例代码: home.ftl
<#extends "layout.ftl">
<@block name="title">首页</@block>
<@block name="main">
<h2>欢迎访问首页</h2>
<p>这是首页内容。</p>
</@block>
逻辑分析:
-
home.ftl继承自layout.ftl,而layout.ftl又继承自base.ftl。 - 每一层模板都可以定义自己的
block,并允许下层模板覆盖。 - 这种结构非常适合构建大型网站的布局层级。
6.2.2 组件化模板的构建与调用
组件化开发是现代 Web 开发的重要理念,Freemarker 支持将常用模板片段封装为可复用的组件,通过宏(macro)或 include 指令调用。
使用宏(macro)构建组件
示例:定义一个按钮组件 components/button.ftl
<#macro button text link>
<a href="${link}" class="btn">${text}</a>
</#macro>
示例:在页面中使用组件
<#import "components/button.ftl" as btn>
<@btn.button text="点击我" link="/click" />
使用 include 调用组件
示例:定义一个头部组件 partials/header.ftl
<header>
<h1>网站头部</h1>
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/about">关于我们</a></li>
</ul>
</nav>
</header>
示例:在页面中使用组件
<#include "partials/header.ftl">
对比分析:
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
macro | 支持参数传递,高度复用 | 仅适用于结构固定、样式统一的组件 | 按钮、表单控件等 |
include | 简单易用,适合静态内容 | 无法传参,灵活性较差 | 头部、底部、侧边栏等 |
使用 Mermaid 流程图展示模板继承与复用流程:
graph TD
A[base.ftl] --> B(layout.ftl)
B --> C(home.ftl)
D[button.ftl] --> E[组件调用]
F[header.ftl] --> G[页面包含]
C --> H[最终渲染页面]
E --> H
G --> H
逻辑说明:
-
base.ftl是基础模板,定义页面骨架。 -
layout.ftl在继承base.ftl的基础上添加布局结构。 -
home.ftl继承layout.ftl,并填充具体内容。 -
button.ftl和header.ftl是组件模板,分别通过宏和 include 被调用。 - 最终渲染页面整合了所有模板内容,形成完整的 HTML 页面。
6.2.3 模板复用的优化策略
在实际项目中,频繁的模板调用可能影响性能。以下是一些优化建议:
- 模板缓存配置 :启用 Freemarker 的模板缓存机制,避免每次请求都重新解析模板。
- 减少嵌套层级 :控制模板继承层级,避免过多的嵌套导致维护困难。
- 使用
nocache参数 :对于动态性强的模板部分,可使用<#include "template.ftl" nocache>避免缓存污染。 - 组件化管理 :将常用组件统一管理,提高复用率和可维护性。
通过本章内容,我们系统地了解了 Freemarker 的模板继承机制与复用技巧。从基础的 extends 与 block 指令,到多层继承、组件化构建,再到性能优化策略,这些内容为构建结构清晰、易于维护的模板系统提供了坚实基础。下一章将继续深入 Freemarker 与 Jeecms 的整合开发实践。
7. Freemarker与Jeecms整合开发流程
Jeecms 是一个基于 Freemarker 模板引擎构建的内容管理系统(CMS),它将 Freemarker 的强大模板能力与 CMS 的内容管理功能深度整合。通过本章的学习,开发者将掌握如何在 Jeecms 环境中进行模板开发、页面布局设计、功能扩展与部署上线,理解 Jeecms 特有的标签系统及其嵌套使用方式,以及如何优化模板性能与实现热加载机制。
7.1 Jeecms标签基础与嵌套应用
Jeecms 提供了丰富的标签库(Taglib),这些标签是 Freemarker 模板中调用后端数据和服务的主要方式。掌握这些标签的语法与使用方式是开发 Jeecms 模板的关键。
7.1.1 标签的语法结构与参数传递
Jeecms 的标签本质上是通过 Freemarker 的宏(macro)和指令(directive)实现的。标签的调用方式通常如下:
[@cms_tag_name param1="value1" param2="value2"/]
其中 cms_tag_name 是标签名称, param1 , param2 是传入的参数,例如:
<!-- 获取站点首页文章列表 -->
[@cms_article_list siteId=1 count=10 /]
| 参数名 | 类型 | 描述 |
|---|---|---|
| siteId | Integer | 站点ID |
| count | Integer | 返回文章条数 |
标签的返回值通常是一个数据集(如 List),可以在模板中遍历输出:
<ul>
[@cms_article_list siteId=1 count=10; article]
<li>${article.title}</li>
[/@cms_article_list]
</ul>
7.1.2 标签嵌套与组合使用的最佳实践
Jeecms 支持多个标签的嵌套使用,可以实现更复杂的数据处理逻辑。例如:
<!-- 嵌套标签:获取文章分类下的文章列表 -->
[@cms_category_list siteId=1; category]
<h3>${category.name}</h3>
<ul>
[@cms_article_list categoryId=category.id count=5; article]
<li>${article.title}</li>
[/@cms_article_list]
</ul>
[/@cms_category_list]
注意事项:
- 标签嵌套层级不宜过深,建议控制在三层以内,以提高可读性和性能。
- 避免在循环中频繁调用数据库查询标签,应尽量通过一次查询获取全部数据。
7.2 网站布局与分页功能实现
Jeecms 支持基于 Freemarker 的模板继承机制,可以构建统一的页面布局结构。同时,分页功能是内容展示中不可或缺的部分。
7.2.1 页面布局的设计与模板划分
Jeecms 推荐使用模板继承来实现页面布局统一,结构如下:
<#-- layout.ftl -->
<html>
<head>
<title><@block name="title">默认标题</@block></title>
</head>
<body>
<header>
<@block name="header">
<h1>默认头部</h1>
</@block>
</header>
<main>
<@block name="content">
默认内容区域
</@block>
</main>
<footer>
<@block name="footer">
<p>默认页脚</p>
</@block>
</footer>
</body>
</html>
子模板继承父模板并重写块内容:
<#-- home.ftl -->
<@extends name="layout.ftl"/>
<@block name="title">首页</@block>
<@block name="header"><h1>Jeecms 首页</h1></@block>
<@block name="content">
<p>这里是首页内容</p>
</@block>
7.2.2 分页功能的实现与性能优化
Jeecms 提供了内置的分页标签,使用方式如下:
<!-- 分页文章列表 -->
[@cms_article_page siteId=1 pageSize=5; page]
<ul>
<#list page.list as article>
<li>${article.title}</li>
</#list>
</ul>
<div class="pagination">
<#if page.hasPre>
<a href="?page=${page.prePage}">上一页</a>
</#if>
<#if page.hasNext>
<a href="?page=${page.nextPage}">下一页</a>
</#if>
</div>
[/@cms_article_page]
性能优化策略:
- 合理设置 pageSize ,避免一次性加载过多数据。
- 使用缓存机制对分页数据进行缓存,减少数据库访问。
- 避免在分页中频繁执行复杂计算逻辑。
7.3 整合开发流程与部署实践
从模板开发到上线部署,Jeecms 提供了一套完整的开发流程支持,包括本地开发、测试、上线及模板热加载等机制。
7.3.1 模板开发、测试与上线流程
-
开发阶段 :
- 在本地或测试环境中创建模板文件,放置在jeecms/template/目录下。
- 使用 Freemarker 语法和 Jeecms 标签进行页面构建。
- 通过浏览器访问测试页面,验证模板输出。 -
测试阶段 :
- 使用 Jeecms 后台模板管理功能上传模板文件。
- 切换站点模板为新开发的模板。
- 测试不同设备下的页面适配情况和功能完整性。 -
上线阶段 :
- 模板测试无误后,上传至生产环境。
- 在 Jeecms 后台配置模板为站点默认模板。
- 清除缓存并刷新页面以确保最新模板生效。
7.3.2 缓存机制与模板热加载配置
Jeecms 支持模板缓存机制,以提高页面加载速度。但开发阶段建议关闭缓存以便实时看到修改效果。
缓存配置示例(freemarker.properties):
# 缓存开启/关闭
template_update_delay=0 # 0 表示每次请求都重新加载模板(开发环境)
# template_update_delay=3600 # 1小时更新一次(生产环境)
# 缓存大小
cache_storage=strong:200, soft:250
热加载配置流程:
- 修改
jeecms/config/application.yml中的模板路径为本地开发目录。 - 启动开发服务器(如 Tomcat 或 Spring Boot DevTools)。
- 修改模板文件后,无需重启服务即可看到更新效果。
graph TD
A[模板开发] --> B[本地测试]
B --> C{是否通过测试?}
C -->|是| D[上传生产环境]
C -->|否| E[修改模板并重新测试]
D --> F[配置缓存机制]
F --> G[上线部署]
G --> H[热加载配置]
简介:Freemarker是一个广泛应用于Java Web开发的模板引擎,能够有效分离业务逻辑与视图层,提升代码可维护性。本文档系统讲解Freemarker的基础语法、高级功能及与Jeecms结合的模板开发技巧。通过学习,开发者可以掌握变量表达式、指令使用、模板继承、自定义标签与函数等核心技能,并熟练运用Jeecms标签实现网站布局、数据展示、分页处理等功能。配套学习资源帮助从入门到实战全面提升模板开发能力。
Freemarker与Jeecms模板开发实战指南
20万+

被折叠的 条评论
为什么被折叠?



