FreeMarker手册全篇

给你一囗甜甜゛ 提交于 2019-11-28 15:50:43

FreeMarker 手册
用于 FreeMarker 2.3.19

Translated By Nan Lei
南磊 译
Copyright:
The Chinese translation of the FreeMarker Manual by Nan Lei is licensed under a Creative
Commons Attribution 3.0 Unported License (see http://creativecommons.org/licenses/by/3.0/ ).
This licence only applies to the Chinese translation, not to the original (English) FreeMarker
Manual.
版权说明:
FreeMarker 中文版手册由南磊翻译,本文档基于 Creative Commons Attribution 3.0
Unported 授权许可(参见 http://creativecommons.org/licenses/by/3.0/deed.zh)
本许可仅应用于中文版,不对原版英文手册。
(译者联系方式为:nanlei1987@gmail.com 或 http://weibo.com/nanlei1987)
目录
前言… 7
什么是 FreeMarker?… 7
我们应该阅读什么内容?… 8
文档规约… 8
联系我们… 8
几点说明… 9
第一部分 模板开发指南… 10
第一章 模板开发入门 … 10
1.1 简介 … 10
1.2 模板 + 数据模型 = 输出… 10
1.3 数据模型一览 … 11
1.4 模板一览 … 14
第二章 数值和类型… 20
2.1 基本内容 … 20
2.2 类型 … 22
第三章 模板 … 26
3.1 总体结构 … 26
3.2 指令 … 27
3.3 表达式… 28
3.4 插值 … 43
第四章 其它 … 46
4.1 自定义指令… 46
4.2 在模板中定义变量 … 51
4.3 命名空间 … 54
4.4 空白处理 … 57
4.5 替换(方括号)语法… 60
第二部分 程序开发指南… 62
第一章 程序开发入门 … 62
1.1 创建配置实例 … 62
1.2 创建数据模型 … 62
1.3 获得模板 … 63
1.4 合并模板和数据模型… 64
1.5 将代码放在一起… 64
第二章 数据模型 … 66
2.1 基本内容 … 66
2.2 标量 … 66
2.3 容器 … 67
2.4 方法 … 68
2.5 指令 … 69
2.6 节点变量 … 75
2.7 对象包装 … 76
第三章 配置 … 80
3.1 基本内容 … 80
3.2 共享变量 … 80
3.3 配置信息 … 81
3.4 模板加载 … 83
3.5 错误控制 … 86
第四章 其它 … 90
4.1 变量 … 90
4.2 字符集问题… 90
4.3 多线程… 92
4.4 Bean 的包装… 92
4.5 日志 … 98
4.6 在 Servlet 中使用 FreeMarker… 99
4.7 为 FreeMarker 配置安全策略…106
4.8 遗留的 XML 包装实现…107
4.9 和 Ant 一起使用 FreeMarker…111
4.10 Jython 包装器…112
第三部分 XML 处理指南…114
前言…114
第一章 揭示 XML 文档…115
1.1 节点树…115
1.2 将 XML 放到数据模型中…117
第二章 必要的 XML 处理…119
2.1 通过例子来学习…119
2.2 形式化描述…126
第三章 声明的 XML 处理…131
3.1 基础内容 …131
3.2 详细内容 …133
第四部分 参考文档…136
第一章 内建函数参考文档 …136
1.1 处理字符串的内建函数 …136
1.2 处理数字的内建函数…150
1.3 处理日期的内建函数…153
1.4 处理布尔值的内建函数 …158
1.5 处理序列的内建函数…158
1.6 处理哈希表的内建函数 …164
1.7 处理节点(XML)的内建函数…164
1.8 很少使用的和专家级的内建函数 …166
第二章 指令参考文档 …170
2.1 if,else,elseif 指令 …170
2.2 switch,case,default,break 指令…172
2.3 list,break 指令…173
2.4 include 指令 …174
2.5 import 指令…177
2.6 noparse 指令…178
2.7 compress 指令…179
2.8 escape,noescape 指令…180
2.9 assign 指令…182
2.10 global 指令 …184
2.11 local 指令 …185
2.12 setting 指令…185
2.13 用户自定义指令(<@…>)…187
2.14 macro,nested,return 指令 …189
2.15 function,return 指令…193
2.16 flush 指令…195
2.17 stop 指令…195
2.18 ftl 指令…196
2.19 t,lt,rt 指令…197
2.20 nt 指令…198
2.21 attempt,recover 指令…199
2.22 visit,recurse,fallback 指令…200
第三章 特殊变量参考文档 …205
第四章 FTL 中的保留名称…207
第五章 废弃的 FTL 结构…208
5.1 废弃的指令列表…208
5.2 废弃的内建函数列表…208
5.3 老式的 macro 和 call 指令…208
5.4 转换指令 …210
5.5 老式 FTL 语法…211
5.6 #{…}式的数字插值…212
第五部分 附录…214
附录 A FAQ …214

  1. JSP 和 FreeMarker 的对比…214
  2. Velocity 和 FreeMarker 的对比…215
  3. 为什么 FreeMarker 对 null-s 和不存在的变量很敏感,如何来处理它? …215
  4. 文档编写了特性 X,但是好像 FreeMarker 并不知道它,或者它的行为和文档描
    述的不同,或者一个据称已经修改的 BUG 依然存在。…216
  5. 为什么 FreeMarker 打印奇怪的数字数字格式(比如 1,000,000 或 1 000 000 而不
    是 1000000)?…216
  6. 为什么 FreeMarker 会打印不好的小数和/或分组分隔符号(比如 3.14 而不是 3,14)
    …217
  7. 为什么当我想用如格式打印布尔值时,FreeMarker 会抛出错误,又如何来修正
    呢?…217
  8. FreeMarker 标签中的<和>混淆了编辑器或 XML 处理器,应该怎么做?…217
  9. 什么是合法的变量名?…217
  10. 如何使用包含空格,或其他特殊字符的变量(宏)名? …218
  11. 当我试图使用 JSP 客户标签时为什么会得到非法参数异常:形式参数类型不匹
    配?…218
  12. 如何像 jsp:include 一样的方式引入其它的资源?…219
  13. 如 何 给 普 通 Java 方 法 / TemplateMethodModelEx/
    TemplateTransformModel/ TemplateDirectiveModel 的实现传递普通
    java.lang.* / java.util.*对象的参数? …219
  14. 为什么在 myMap[myKey]表达式中不能使用非字符串的键?那现在应该怎么
    做?…220
  15. 当使用?keys/?values 遍历 Map(哈希表)的内容时,得到了混合真正 map
    条目的 java.util.Map 的方法。当然,只是想获取 map 的条目。…221
  16. 在 FreeMarker 的模板中如何改变序列(lists)和哈希表(maps)?…221
  17. 关于 null 在 FreeMarker 模板语言是什么样的?…222
  18. 我该怎么在表达式(作为另外一个指令参数)中使用指令(宏)的输出? 223
  19. 在输出中为什么用“?”来代替字符 X?…223
  20. 在模板执行完成后,怎么在模板中获取计算过的值?…224
  21. 我能允许用户上传模板吗?又如何保证安全呢?…224
  22. 如何在 Java 语言中实现方法或宏而不是在模板语言中?…225
  23. 为什么 FreeMarker 的日志压制了我的应用程序?…225
  24. 在基于 Servlet 的应用程序中,如何在模板执行期间发生错误时,展示一个友
    好的错误提示页面,而不是堆栈轨迹?…226
  25. 我正使用一个可视化的 HTML 割裂模板标记的编辑器。你们可以改变模板语言
    的语法来兼容我的编辑器么?…226
  26. FreeMarker 有多快?真的是 2.X 版本的要比 1.X 版本(经典的 FreeMarker)的慢
    吗?…226
  27. 我的 Java 类怎么才能获取到关于模板结构的信息(比如所有变量的列表)?
    …227
  28. 你会一直提供向后的兼容性吗? …227
  29. 如果我们把 FreeMarker 和我们的产品一起发行,我们需要发布我们产品的源代
    码么?…228
    附录 B 安装 FreeMarker…229
    附录 C 构建 FreeMarker…230
    附录 D 版本…231
    2.3.18 版…231
    2.3.17 版…233
    2.3.16 版…235
    2.3.15 版…236
    2.3.14 版…237
    2.3.13 版…238
    2.3.12 版…238
    2.3.11 版…239
    2.3.10 版…240
    2.3.9 版…241
    2.3.8 版…242
    2.3.7 版…242
    2.3.7 RC1 版…243
    2.3.6 版…244
    2.3.5 版…244
    2.3.4 版…245
    2.3.3 版…246
    2.3.2 版…247
    2.3.1 版…248
    2.3 版…250
    2.2.8 版…262
    2.2.7 版…263
    2.2.6 版…263
    2.2.5 版…264
    2.2.4 版…264
    2.2.3 版…265
    2.2.2 版…265
    2.2.1 版…266
    2.2 版…266
    2.1.5 版…275
    2.1.4 版…275
    2.1.3 版…275
    2.1.2 版…276
    2.1.1 版…276
    2.1 版…277
    2.01 版…281
    2.0 版…281
    2.0 RC3 版…282
    2.0 RC2 版…283
    2.0 RC1 版…284
    附录 E 许可…287
    词汇表…288
    前言
    什么是 FreeMarker?
    FreeMarker 是一款模板引擎:即一种基于模板、用来生成输出文本(任何来自于 HTML
    格式的文本用来自动生成源代码)的通用工具。它是为 Java 程序员提供的一个开发包,或者
    说是一个类库。它不是面向最终用户的,而是为程序员提供的一款可以嵌入他们所开发产品
    的应用程序。
    FreeMarker 实际上是被设计用来生成 HTML 页面,尤其是通过实现了基于 MVC(Model
    View Controller,模型-视图-控制器)模式的 Java Servlet 应用程序。使用 MVC 模式的动态页面
    的设计构思使得你可以将前端设计师(编写 HTML 页面的人员)从程序员中分离出来。那么,
    所有人各司其职,发挥其最擅长的一面。网页设计师可以改写页面的显示效果而不受程序员
    编译代码的影响,因为应用程序的逻辑(这里是 Java 程序)和页面设计(这里是 FreeMarker 模
    板)已经被分开了。页面模板代码不会受到复杂程序代码的影响。这种分离的思想即便对一
    个程序员和页面设计师是同一个人的项目来说也都是非常有用的,因为分离使得代码保持简
    洁而且易于维护。
    尽管 FreeMarker 也拥有一些编程能力,但是它却不像 PHP 那样,是的一种全面的编程
    语言。反而,Java 程序准备的数据来进行显示(比如 SQL 数据库查询),FreeMarker 仅仅是使
    用模板生成文本页面来呈现已经准备好的数据而已。
    FreeMarker 不是 Web 开发的应用程序框架。它是一个适用于 Web 应用程序框架中的组
    件,但是 FreeMarker 引擎本身并不知道 HTTP 协议或 Java Servlet 的存在。它仅仅来生成文
    本内容。既然是这样,它也非常适用于非 Web 应用程序的开发环境。只是要注意的是,我
    们使用 FreeMarker 作为视图层的组件,是为了给诸如 Struts 这样的 Model 2 应用框架提供现
    成的解决方案。
    FreeMarker 本身是免费的,它基于 BSD 规则的许可协议。它也是 OSI 认证的开源软件。
    OSI 认证是开源倡议的认证标识。
    我们应该阅读什么内容?
    如果你是一名…
    前端设计师,首先,你应该阅读的是模板开发指南部分,然后如果有需要的话,可以再
    阅读参考手册来获取更多的技术细节。
    程序员,那么,你应该先阅读模板开发指南部分,然后是程序开发指南部分,最后如果
    需要的话可以阅读参考手册来获取更多技术细节。
    本文档规约
    变量名,模板代码段,Java 类名等用如下格式书写,如:foo。
    如果需要具体值来代替某些内容,那么用斜体书写,如:Hello yourName!
    模板示例如下书写:
    数据对象示例如下书写:
    输出数据示例如下书写:
    程序示例如下书写:
    在面向页面设计师和程序员所编写的章节中代码段给程序员这样写:这只是对程序员而
    言的。
    这样来强调新名词:一些新名词
    联系我们
    获取最新版本的 FreeMarker,订阅邮件请访问 FreeMarker 主页:http://freemarker.org
    如果你需要帮助或者有好的建议,可以使用邮件(邮件文件可以免费搜索)或者 Web 论坛。
    如果你想报告一个 Bug,请使用 Web 的 Bug 跟踪系统或者是邮件。查阅这些内容请访问
    http://freemarker.org。同时,要注意我们有一个 FAQ 和索引,你可以使用它们。
    Something
    Something
    Something
    Something
    几点说明
    因为英文版文档的作者是匈牙利人,其母语非英语,那么在这种情况的翻译过程,可能
    会有错误存在,作者结合自身多年对 FreeMarker 的实践力求精准,但因个人才疏学浅,水
    平有限,恳请读者批评指正。
    手册的更新根据大家的反馈随时进行,但只在有阶段性成果时公开发布修正版本,并在
    FreeMarker 2.4 版本研发完整后,会及时联系原作者获取新特性以便修改。
    本翻译是免费的,您可以自由下载和传播,不可用于任何商业行为。但文档版权归译者
    所有,原版归 FreeMarker 项目组所有,您可以引用其中的描述,但必须指明出处。如需用
    于商业行为,您必须和原作者取得联系。
    如果你发现英文原版任何错误(包括语法错误,错别字)或者是在文档中找到一些误导
    或混淆错误,也可以是其他的建议,或是咨询 FreeMarker 中的问题,您可以联系原作者。
    E-mail:ddekany@freemail.hu
    关于本文档的翻译错误(包括语法错误,错别字)或中文技术交流,可以联系译者:
    nanlei1987@gmail.com 或 http://weibo.com/nanlei1987,我们共同研究,共同进步。
    第一部分 模板开发指南
    第一章 模板开发入门
    1.1 简介
    本章内容是对 FreeMarker 进行简略的介绍,后续章节中将会详细展开。不过没有关系,
    只要你阅读了本章节的内容后,你就能够编写简单,但却很有用的 FreeMarker 模板程序了。
    1.2 模板 + 数据模型 = 输出
    假设你在一个在线商店的应用系统中需要一个 HTML 页面,和下面这个页面类似:
    在这里,比方说用户名(所有的”Big Joe”),应该是登录这个网页的访问者的名字,并
    且最新产品的数据应该来自于数据库,这样它们才可以随时进行更新。这样的情况下,你不
    能在 HTML 页面中直接输入登录用户的用户名,最新产品的 URL 和名称,你不能使用静态的
    HTML 代码,那样是不能即时改变的。
    对于这个问题,FreeMarker 的解决方案是使用模板来代替静态的 HTML 文本。模板文件
    同样是静态的 HTML 代码,但是除了这些 HTML 代码外,代码中还包括了一些 FreeMarker
    指令元素,这些指令就能够做到动态效果。
Welcome!

Welcome Big Joe!

Our latest product: green mouse!

Welcome!

Welcome ${user}!

Our latest product: ${latestProduct.name}!

这个模板存放在 Web 服务器上,看上去像是静态的 HTML 页面。但不管何时,只要有 人来访问这个页面,FreeMarker 将会介入执行,然后动态转换模板,用最新的数据内容替换 模板中${…}的部分(例如:用 Big Joe 或者其他的访问者的用户名来代替${user}),生成普通 的 HTML 文本并发送结果到访问者的 Web 浏览器中去显示。所以访问者的 Web 浏览器会接 收到类似于第一个 HTML 示例的内容(也就是说,显示普通的 HTML 文本而没有 FreeMarker 的指令),因为浏览器也不会感知到 FreeMarker 在服务器端被调用了。模板文件本身(存储 在 Web 服务器端的文件)在这个过程中也不会改变什么,所以这个转换过程发生在一次又 一次的访问中。这样就保证了显示的信息总是即时的。 现在,也许你已经注意到,该模板并没有包含关于如何找出当前的访问者是谁,或者是 如何去查询数据库中查找最新的产品的指令。它似乎已经知道了这些数据是什么。事实也确 实是这样的,在 FreeMarker 背后(确切的说是在 MVC 模式的背后)的重要思想就是表现逻 辑和业务逻辑相分离。在模板中,只是处理显示相关的问题,也就是视觉设计问题和格式问 题。所准备要显示的数据(如用户名等)与 FreeMarker 无关,这通常是使用 Java 语言或其 他目的语言来编写的程序。所以模板开发人员不需要关心这些数值是如何计算出来的。事实 上,在模板保持不变的同时,这些数值的计算方式可以发生根本的变化。而且,除了模板外, 页面外观发生的变化可以完全不触碰其他任何东西。当模板开发人员和程序员是不同一个人 的时候,分离带来的好处更是显而易见的。 FreeMarker(还有模板开发人员)并不关心数据是如何计算出来的,FreeMarker 只是知 道真实的数据是什么。模板能用的所有数据被包装成 data-model 数据模型。数据模型的创 建是通过已经存在的程序计算得到的。至于模板开发人员,数据模型像是树形结构(比如硬 盘上的文件夹和文件),正如本例中的数据模型,就可以如下形式来描述: (为了避免误解:数据模型并不是文本文件,上面所描述的只是一种数据模型的表现形式。 它来自于 Java 对象,但这会成为 Java 程序员要面对的问题。) 比较之前你在模板中看到的${user}和${latestProduct.name}。作为一种 比喻:数据模型就像计算机文件系统上的内容:根 root 和 latestProduct 对应目录 (文件夹),user,url 和 name 对应文件。url 和 name 在 latestProduct 目录 中,所以 latestProduct.name 就像是说 latestProduct 目录的 name 一样。 但是我所说的,这仅仅是个比喻,这里并没有真实的文件和目录。 概括地讲,模板和数据模型是 FreeMarker 所需,并用来生成输出内容的(比如之前展 示的 HTML):模板+数据模型=输出 1.3 数据模型一览 正如你看到的,数据模型基本结构是树状的。这棵树可以复杂而且有很大的深度,比如: (root) | +- user = "Big Joe" | +- latestProduct | +- url = "products/greenmouse.html" | +- name = "green mouse" 上图中变量扮演目录的角色(根 root,animal,mouse,elephant,python, whatnot)被称为 hash 哈希表。哈希表通过可查找的名称(例如:”animal”, ”mouse”, ”price”) 来访问存储的其他变量(如子变量)。 如果仅存储单值的变量(size,price,text 和 because)则它们被称为 scalars 标量。 如果要在模板中使用子变量,那应该从根 root 开始指定它的路径,每级之间用点来分 隔。要访问 price 和 mouse 的话,应该从根开始,先是 animals,然后是 mouse, 最后是 price,所以应该这样写:animals.mouse.price。当放置${…}这种特定 代码在表达式的前后时,我们就告诉 FreeMarker 在那个位置上要来输出对应的文本。 sequences 序列也是一种非常重要的变量,它们和哈希表变量相似,但是它们不存储所 包含变量的名称,而是按顺序存储子变量。这样,就可以使用数字索引来访问这些子变量。 在这种数据模型中,animal 和 whatnot.fruits 就是序列: (root) | +- animals | | | +- mouse | | | | | +- size = "small" | | | | | +- price = 50 | | | +- elephant | | | | | +- size = "large" | | | | | +- price = 5000 | | | +- python | | | +- size = "medium" | | | +- price = 4999 | +- test = "It is a test" | +- whatnot | +- because = "don't know" 可以使用数组的方括号方式来访问一个序列的子变量。索引从零开始(从零开始是程序 员写代码的传统习惯),那么就意味着序列第一项的索引是 0,第二项的索引是 1,并以此类 推。要得到第一个动物的名称的话,那么就应该这么写代码:animals[0].name。要 得到 whatnot.fruits(就是”banana”这个字符串)的第二项,那么就应该这么来 写:whatnot.fruits[1]。 标量可以分为如下类别: 字符串:这是文本类型,字符的任意序列,比如”m”,“o”,“u”,“s”,“e”这些,而且 name-S 和 size-S 也是字符串范畴。 (root) | +- animals | | | +- (1st) | | | | | +- name = "mouse" | | | | | +- size = "small" | | | | | +- price = 50 | | | +- (2nd) | | | | | +- name = "elephant" | | | | | +- size = "large" | | | | | +- price = 5000 | | | +- (3rd) | | | +- name = "python" | | | +- size = "medium" | | | +- price = 4999 | +- whatnot | +- fruits | +- (1st) = "orange" | +- (2nd) = "banana" 数字:这是数字值类型,比如 price-S 这些。在 FreeMarker 中字符串”50”和数字 50 是两种完全不同的类型。前者只是两个字符的序列(这恰好是我们可以读的一个数字),而 后者是一个可以在算数运算中直接被使用的数值。 日期/时间:这是时间日期类型。例如动物被捕捉的日期,或商店开始营业的时间。 布尔值:对应对/错(是/否,开/关等)这样仅仅代表正反的值。比如动物可以有一个 受保护(protected,译者注)的子变量,这个变量存储这个动物是否被保护起来。 总结: 数据模型可以被看做是树状结构的。 标量存储单一的值,这种类型的值可以是字符串,数字,日期/时间或者是布尔值。 哈希表是存储变量和与其相关且有唯一标识名称变量的容器。 序列是存储有序变量的容器。存储的变量可以通过数字索引来检索,索引通常从零开始。 1.4 模板一览 1.4.1 简介 最简单的模板是普通 HTML 文件(或者是其他任何文本文件—FreeMarker 本身不属于 HTML)。当客户端访问页面时,FreeMarker 要发送 HTML 代码至客户端浏览器端显示。如果 想要页面动起来,就要在 HTML 中放置能被 FreeMarker 所解析的特殊部分。 ${…}:FreeMarker 将会输出真实的值来替换花括号内的表达式,这样的表达式被称为 interpolations 插值,可以参考第上面示例的内容。 FTL tags 标签(FreeMarker 模板的语言标签):FTL 标签和 HTML 标签有一点相似,但是 它们是 FreeMarker 的指令而且是不会直接输出出来的东西。这些标签的使用一般以符号# 开头。(用户自定义的 FTL 标签使用@符号来代替#,但这是更高级的主题内容了,后面会详 细地讨论) Comments 注释:FreeMarker 的注释和 HTML 的注释相似,但是它用<#--和-->来分 隔的。任何介于这两个分隔符(包含分隔符本身)之间内容会被 FreeMarker 忽略,就不会 输出出来了。 其他任何不是 FTL 标签,插值或注释的内容将被视为静态文本,这些东西就不会被 FreeMarker 所解析,会被按照原样输出出来。 directives 指令:就是所指的 FTL 标签。这些指令在 HTML 的标签(如和
)和 HTML 元素(如 table 元素)中的关系是相同的。(如果现在你还不能区 分它们,那么把“FTL 标签”和“指令”看做是同义词即可。) 1.4.2 指令示例 尽管 FreeMarker 有很多指令,作为入门,在快速了解过程中我们仅仅来看三个最为常 用的指令。 1.4.2.1 if 指令 使用 if 指令可以有条件地跳过模板的一部分,这和程序语言中 if 是相似的。假设在第 一个示例中,你只想向你的老板 Big Joe(而不是其他人)问好,就可以这样做: 在这里,我们告诉 FreeMarker,我们尊敬的领导才是 if 条件中那唯一的 user 变量值, 当它和”Big Joe”相同时才显示出来。那么,当 condition 的判断结果为 false(布尔 值)时,在<#if condition>和

// 一个方法
public double sin(double x) {
return Math.sin(x);
}
}
± theList
|
± (1st) = “red”
|
± (2nd) = “green”
|
± (3rd) = “blue”
我们可以这样把它和模板合并:
将会输出:
之前我们已经看到了,我们使用 java.util.HashMap 作为根哈希表,而不是
SimpleHash 或其他特定的 FreeMarker 类。因为 Template.process(…)自动包
装了给定的数据模型参数的对象,所以它才会起作用。它使用受 Configuration 级设
置的对象包装器,object_wrapper(除非明确指定一个 ObjectWrapper 作为它
的参数)。因此,编写简单的 FreeMarker 应用程序就不需要知道 TemplateModel-s 了。
注意根的类型不需要一定是 java.util.Map 。它也可以是实现了
TemplateHashModel 接口的被包装的对象。
object_wrapper 设置的默认值是 ObjectWrapper.DEFAULT_WRAPPER。
如果想改变它,比如换成 ObjectWrapper.BEANS_WRAPPER,那么可以这样来配置
FreeMarker 引擎(在其它线程开始使用它之前):
要注意我们可以在这里设置任何对象实现接口 ObjectWrapper,当然也可以用来
设置你自己定义的实现类。
对于包装了基本Java容器类型(比如java.util.Map-s和java.util.List-s)
的 TemplateModel 实现类,常规是它们使用像它们父容器那样的相同对象包装器来包
装它们的子变量。从技术上讲,它们是被父容器(它对所创建的子类有全部的控制器)实例
化的,因为父容器创建了它们,所以它们使用和父容器一样的对象包装器。如果
BEANS_WRAPPER 用来包装根哈希表,那么它也会被用来包装子变量(子变量的子变量
也是如此,以此类推)。这个之前看到的 theMap.anotherString 是同样的现象。
(root)
|
± theString = “wombat”
|
± theObject
|
± name = “green mouse”
|
± price = 1200
|
± number sin(number)
${theObject.name}
${theObject.price}
KaTeX parse error: Expected 'EOF', got '#' at position 1006: …定义指令使用时需要用@来代替 #̲): 名称 类 capture…{badVar}b
a[ERROR: Expression badVar is undefined on line 1, column 4 in
test.ftl.]b
aKaTeX parse error: Expected 'EOF', got '#' at position 21: …" + badVar}b a<#̲if badVar>Foo</…{badVar}" == “foobar”>Foo</#if>b
a
<#if true>
Foo
${badVar}
Bar
</#if>
c
a
<@test />
b
<#macro test>
Foo
KaTeX parse error: Expected 'EOF', got '#' at position 17: …badVar} Bar </#̲macro> a Foo …{obj.method(args)} 形式的语法被安全地调用。
java.util.Map 实例的模型仍然实现了 TemplateMethodModelEx 接口,
作为调用它们 get()方法的一种方式。正如前面所讨论的那样,你可以使用哈希表功能来
访问“get”方法,但是它有一些缺点:因为第一个属性和方法名会被键名来检查,所以执
行过慢;和属性,方法名相冲突的键将会被隐藏;最终这种方法中你只可使用 String 类
型的键。对比一下,调用 model(key) 方法,将直接翻译为 model.get(key):因
为没有属性和方法名的查找,速度会很快;不容易被隐藏;最终对非字符串的键也能正常处
理,因为参数没有被包装,只是被普通的方法调用。实际上,Map 中的 model(key)和
model.get(key)是相等的,只是写起来很短罢了。
java.util.ResourceBundle 类的模型也实现了
TemplateMethodModelEx 接口,作为一种访问资源和信息格式化的方便形式。对资
源包的单参数调用,将会取回名称和未包装参数的 toString()方法返回值一致的资源。
对资源包的多参数调用的情况和单参数一样,但是它会将参数作为格式化的模式传递给
java.text.MessageFormat,在第二个和后面的作为格式化的参数中使用未包装的
值。MessageFormat 对象将会使用它们原本的本地化资源包来初始化。
4.4.8 解包规则
当从模板中调用 Java 方法时,它的参数需要从模板模型转换回 Java 对象。假设目标类
型(方法常规参数被声明的类型)是用来 T 代表的,下面的规则将会按下述的顺序进行依
次尝试:
 对包装器来说,如果模型是空模型,就返回 Java 中的 null。
 如果模型实现了 AdapterTemplateModel 接口,如果它是 T 的实例,或者
它是一个数字而且可以使用数字强制转换成 T ,那么
model.getAdaptedObject(T)的结果会返回。由 BeansWrapper 创建的所
有方法是 AdapterTemplateModel 的实现,所以由 BeansWrapper 为基本的 Java 对象
创建的展开模型通常不如原本的 Java 对象。
 如果模型实现了已经废弃的 WrapperTemplateModel 接口,如果它是 T 的
实 例 , 或 者 它 是 一 个 数 字 而 且 可 以 使 用 数 字 强 制 转 换 成 T ,那么
model.getWrappedObject()方法的结果会返回。
 如 果 T 是 java.lang.String 类 型 , 那 么 如 果 模 型 实 现 了
TemplateScalarModel 接口,它的字符串值将会返回。注意如果模型没有
实现接口,我们不能尝试使用 String.valueOf(model)方法自动转换模型到 String 类型。
这里不得不使用内建函数?string 明确地用字符串来处理非标量。
 如果 T 是原始的数字类型或者是可由 T 指定的 java.lang.Number 类型,还
有模型实现了 TemplateNumberModel 接口,如果它是 T 的实例或者是它的
装箱类型(如果 T 是原始类型), 那么它的数字值会返回。否则,如果 T 是一个
Java 内建的数字类型(原始类型或是 java.lang.Number 的标准子类,包括
BigInteger 和 BigDecimal),类型 T 的一个新对象或是它的装箱类型会由
数字模型的适当强制的值来生成。
 如果 T 是 boolean 值或 java.lang.Boolean 类型,模型实现了
TemplateHashModel 接口,那么布尔值将会返回。
 如果 T 是 java.util.Map 类型,模型实现了TemplateHashModel 接口,
那么一个哈希表模型的特殊 Map 表示对象将会返回。
 如 果 T 是 java.util.List 类型,模型实现了
TemplateSequenceModel 接口,那么一个序列模型的特殊 List 表示对象将
会返回。
 如 果 T 是 java.util.Set 类型,模型实现了
TemplateCollectionModel 接口,那么集合模型的一个特殊 Set 表示对象
将会返回。
 如果 T 是 java.util.Collection 或 java.lang.Iterable 类型,
模 型 实 现 了 TemplateCollectionModel 或
TemplateSequenceModel 接口,那么集合或序列模型(各自地)一个特殊
的 Set 或 List 表示对象将会返回。
 如果 T 是 Java 数组类型,模型实现了 TemplateSequenceModel 接口,那
么一个新的指定类型的数组将会创建,它其中的元素使用数组的组件类型作为 T,
递归展开到数组中。
 如 果 T 是 char 或 者 java.lang.Character 类型 , 模 型实 现 了
TemplateScalarModel 接口,它的字符串表示中包含精确的一个字符,那
么一个 java.lang.Character 类型的值将会返回。
 如 果 T 定 义 的 是 java.util.Date 类 型 , 模 型 实 现 了
TemplateDateModel 接口,而且它的日期值是 T 的实例,那么这个日期值
将会返回。
 如果模型是数字模型,而且它的数字值是 T 的实例,那么数字值就会返回。你可
以得到一个实现了自定义接口的 java.lang.Number 类型的自定义子类,也许 T 就是
那个接口。
 如果模型是日期类型,而且它的日期值是 T 的实例,那么日期值将会返回。类似
的考虑为*。
 如果模型是标量类型,而且 T 可以从 java.lang.String 类型来定义,那么
字符串值将会返回。这种情况涵盖 T 是 java.lang.Object, java.lang.Comparable 和
java.io.Serializable 类型。
 如果模型是布尔类型,而且 T 可以从 java.lang.Boolean 类型来定义,那
么布尔值将会返回。和是相同的。
 如 果 模 型 是 哈 希 表 类 型 , 而 且 T 可以从
freemarker.ext.beans.HashAdapter 类型来定义,那么一个哈希表
适配器将会返回。和
是形同的。
 如 果 模 型 是 序 列 类 型 , 而 且 T 可以从
freemarker.ext.beans.SequenceAdapter 类型来定义,那么一个序
列适配器将会返回。和是形同的。
 如 果 模 型 是 集 合 类 型 , 而 且 T 可以从
freemarker.ext.beans.SetAdapter 类型来定义,那么集合的 set 适
配器将会返回。和
是形同的。
 如果模型是 T 的实例,那么模型本身将会返回。这种情况涵盖方法明确地声明一
个 FreeMarker 特定模型接口,而且允许返回指令,当 java.lang.Object 被请求时允
许返回方法和转换的模型。
 意味着没有可能转换的异常被抛出。
4.4.9 访问静态方法
从 BeansWrapper.getStaticModels() 方法返回 的
TemplateHashModel 对象可以用来创建哈希表模型来访问静态方法和任意类型的字
段。
之后你就可以得到模板的哈希表模型,它会暴露所有 java.lang.System 类的静
态方法和静态字段(final 类型和非 final 类型)作为哈希表的键。设想你已经将之前的模型
放到根模型中了:
从现在开始,你可以在模板中使用${File.SEPARATOR}来插入文件分隔符,或者
你可以列出所有文件系统中的根元素,通过:
来进行。
当然,你必须小心这个模型所带来的潜在的安全问题。
你可以给模板作者完全的自由,不管它们通过将静态方法的哈希表放到模板的根模型中,
来使用哪种类的静态方法,如用如下方式:
如果它被用作是以类名为键的哈希表,这个对象暴露的只是任意类的静态方法。那么你
可 以 在 模 板 中 使 用 如
KaTeX parse error: Expected 'EOF', got '#' at position 588: …fileStatics); <#̲list File.listR…{enums[“java.math.RoundingMode”].UP}的表达式。
被暴露的枚举值可以被用作是标量(它们会委派它们的 toString()方法),也可以
用在相同或不同的比较中。
注意在上述的例子中,我们通常使用默认的 BeansWrapper 实例。这是一个方便使
用的静态包装器实例,你可以在很多情况下使用。特别是你想修改一些属性(比如模型缓存,
安全级别,或者是空模型对象表示)时,你也可以自由地来创建自己的 BeansWrapper
实例,然后用它们来代替默认包装器。
4.5 日志
FreeMarker 整合了如下的日志包:SLF4,Apache Commons Logging,Log4J,Avalon LogKit
和 java.util.logging(Java2 平台 1.4 版本之后)。默认情况下,FreeMarker 会按如下顺序来查
找日志包,而且会自动使用第一个发现的包:SLF4J,Apache Commons Logging,Log4J,Avalon,
java.util.logging。然而,如果在 freemarker.log.Logger 类用合适的参数中调用静
态的 selectLoggerLibrary 方法,而且在使用任何 FreeMarker 类之前记录信息,你
可以明确地选择一个日志包,或者关闭日志功能。可以参见 API 文档获取详细信息。
注意:
在 FreeMarker 2.4 版本之前,因为向后兼容性的限制,SLF4J 和 Apache Commons Logging
不会被自动搜索,所以要使用它们其中之一,你必须这样来做:
BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
TemplateHashModel enumModels = wrapper.getEnumModels();
TemplateHashModel roundingModeEnums =
(TemplateHashModel)enumModels.get(“java.math.RoundingMode”);
root.put(“RoundingMode”, roundingModeEnums);
root.put(“enums”,
BeansWrapper.getDefaultInstance().getEnumModels());
import freemarker.log.Logger;

// 重要:这应该在使用其它 FreeMarker 类之前被执行!
Logger.selectLoggerLibrary(Logger.LIBRARY_SLF4J);
//或者 Logger.LIBRARY_COMMONS

我们推荐这样来做,因为从 FreeMarker 2.4 版开始,如果 SLF4J 可用,它会被默认使用,
否则使用 Apache Commons Logging,否则会使用 Log4J。
所有由 FreeMarker 产生的日志信息会被记录到顶级记录器是名为 freemarker 的记
录器中。现在被使用的记录器是:
记录器名称 目标
freemarker.beans 记录 Beans 包装器模块的日志信息。
freemarker.cache 记录模板加载和缓存相关的日志信息。
freemarker.runtime
记录在模板执行期间抛出的模板异常日志信
息。
freemarker.runtime.attempt
记录在模板执行期间抛出的模板异常日志信
息,但是是由 attempt/recover 指令捕捉
的。开启 DEBUG 严重级别来查看异常。
freemarker.servlet
记录由 FreeMarkerServlet 类产生的日
志信息。
freemarker.jsp 记录FreeMarker对JSP支持时打出的日志信息。
你也可以调用 freemarker.log.Logger 类 中 的 静 态 方 法
selectLoggerLibrary,传递一个字符串来用作是前缀,这是上述提到的日志包名称。
如果你想给每个 Web 应用使用不同的日志包,这个基本操作会是很有用的(假设应用程序
使用的是 它 本 地 的 freemarker.jar 包 )。 而 且 你 也 可 以 使 用
Logger.selectLoggerLibrary(Logger.LIBRARY_NONE) 来关闭日志,不
管哪种情况,selectLoggerLibrary 必须在 FreeMarker 记录任何日志信息前面来调
用,否则它就不会有任何效果。
4.6 在 Servlet 中使用 FreeMarker
作为基础的了解,在 Web 应用领域中使用 FreeMarker 和其他没有什么不同。FreeMarker
将输出内容写到你传递给 Template.process 方法的 Writer 对象中,它并不关心
Writer 对象将输出内容打印到控制台或是一个文件中,或是
HttpServletResponse 对象的输出流中。FreeMarker 并不知道 servlets 和 Web;它
仅仅是使用模板文件来合并 Java 对象,之后从它们中间生成输出文本。从这里可知,如何
创建一个 Web 应用程序都随你的习惯来。
但是,你可能想在已经存在的 Web 应用框架中使用 FreeMarker。许多框架都是基于
“Model 2”架构的,JSP 页面来控制显示。如果你使用了这样的框架(不如 Apache Struts),
那么继续阅读本文。对于其他框架请参考它们的文档。
4.6.1 在“Model 2”中使用 FreeMarker
许多框架依照 HTTP 请求转发给用户自定义的“action”类,将数据作为属性放在
ServletContext,HttpSession 和 HttpServletRequest 对象中,之后请
求被框架派发到一个 JSP 页面中(视图层),使用属性传递过来的数据来生成 HTML 页面,
这样的策略通常就是所指的 Model 2 模型。
使用这样的框架,你就可以非常容易的用 FTL 文件来代替 JSP 文件。但是,因为你的
Servlet 容器(Web 应用程序服务器),不像 JSP 文件,它可能并不知道如何处理 FTL 文件,
那么就需要对 Web 应用程序进行一些额外的配置。

  1. 复制 freemarker.jar 到(从 FreeMarker 发布包的 lib 目录中)你的 Web
    应用程序的 WEB-INF/lib 目录下。
  2. 将下面的部分添加到 Web 应用程序的 WEB-INF/web.xml 文件中(调整它是否
    需要)。

    freemarker

    freemarker.ext.servlet.FreemarkerServlet
TemplatePath / NoCache true ContentType text/html; charset=UTF-8 在这之后,你可以像使用 JSP(*.jsp)文件那样使用 FTL 文件(*.ftl)了。(当然 你可以选择除 ftl 之外的扩展名;这只是惯例) 注意 它是怎么工作的?让我们来看看 JSP 是怎么工作的。许多 servlet 容器处理 JSP 时使用一 个映射为*.jsp 的 servlet 请求 URL 格式。这样 servlet 就会接收所有 URL 是以.jsp 结尾的请求,查找请求 URL 地址中的 JSP 文件,内部编译完后交给 Servlet,然后调 template_update_delay 0 default_encoding ISO-8859-1 number_format 0.########## 1 freemarker *.ftl ... FreeMarker MVC Views *.ftl 用生成信息的 serlvet 来 生 成 页 面 。 这 里 为 URL 类型是 *.ftl 映射的 FreemarkerServlet 也是相同功能,只是 FTL 文件不会编译给 Servlet-s,而 是给 Template 对象,之后 Template 对象的 process 方法就会被调用来生成 页面。 比如,代替这个 JSP 页面(注意它使用了 Struts 标签库来保存设计,而不是嵌入可怕的 Java 代码): 你可以使用这个 FTL 文件(使用 ftl 扩展名而不是 jsp): 警告! 在 FreeMarker 中,... 仅仅被视为是静态文本,所以它会按照原本输出出来了,就像其他 XML 或 HTML 标记 一样。JSP 标签也仅仅是 FreeMarker 的指令,没有什么特殊之处,所以你可以使用 FreeMarker 语 法 形式来调用它们,而不是 JSP 语法: <@html.form action="/query">...


<@myRepeatMacro count=4 ; x, last>
${x}. Something… <#if last> This was the last!</#if>
/@myRepeatMacro
注意由自定义指令创建的循环变量数量和分号之后指定的循环变量数量需要不匹配。也
就是说,如果你对重复是否是最后一个不感兴趣,你可以简单来写:
或者你可以:
此外,如果你在分号之后指定更多循环变量而不是自定义指令创建的,也不会引起错误,
只是最后的循环变量不能被创建(也就是在嵌套内容中那些将是未定义的)。尝试使用未定
义的循环变量,就会引起错误(除非你使用如?default 这样的内建函数),因为你尝试
访问了一个不存在的变量。
请参考模板开发指南/其他/定义你自己的指令部分来获取更多内容。
2.13.2.3 位置参数传递
位置参数传递(如<@heading “Preface”, 1/>)是正常命名参数传递(如
<@heading title=“Preface” level=1/>)的速记形式,这里忽略了参数的名
称。如果自定义指令只有一个参数,或者对于经常使用的自定义指令它参数的顺序很好记忆,
速记形式应该被应用。为了应用这种形式,你不得不了解声明的命名参数的顺序(如果指令
只有一个参数这是很琐碎的)。也就是,如果 heading 被创建为<#macro heading
title level>… ,那么 <@heading “Preface”, 1/> 和 <@heading
title=“Preface” level=1/> ( 或 <@heading level=1
title=“Preface”/>;如果你使用参数名称,那顺序就不重要了)是相等的。要注意
位置参数传递现在仅仅支持宏定义。
2.14 macro,nested,return 指令
2.14.1 概要
<#macro name param1 param2 … paramN>

<#nested loopvar1, loopvar2, …, loopvarN>

<#return>

</#macro>
这里:
 name:宏变量的名称,它不是表达式。然而,它可以被写成字符串的形式,如果
<@myRepeatMacro count=4 ; x>
KaTeX parse error: Expected 'EOF', got '#' at position 115: …保留字符时这是很有用的,比如<#̲macro "foo-bar"…{foo}")。
 param1,param2 等: 局部变量的名称,存储参数的值(不是表达式),在=
号后面和默认值(是表达式)是可选的。默认值也可以是另外一个参数,比如
<#macro section title label=title>。
 paramN,最后一个参数,可以可选的包含一个尾部省略(…),这就意味着宏
接受可变的参数数量。如果使用命名参数来调用,paramN 将会是包含给宏的所
有未声明的键/值对的哈希表。如果使用位置参数来调用,paramN 将是额外参数
的序列。
 loopvar1,loopvar2 等:可选的循环变量的值,是 nested 指令想为嵌套
内容创建的。这些都是表达式。
return 和 nested 指令是可选的,而且可以在<#macro>和</#macro>之间被
用在任意位置和任意次数。
没有默认值的参数必须在有默认值参数(paramName=defaultValue)之前。
2.14.2 描述
创建一个宏变量(在当前命名空间中,如果你知道命名空间的特性)。如果你对宏和自
定义指令不了解,你应该阅读模板开发指南/其他/定义你自己的指令部分。
宏变量存储模板片段(称为宏定义体)可以被用作自定义指令。这个变量也存储自定义
指令的被允许的参数名。当你将这个变量作为指令时,你必须给所有参数赋值,除了有默认
值的参数。默认值当且仅当你调用宏而不给参数赋值时起作用。
变量会在模板开始时被创建;而不管 macro 指令放置在模板的什么位置。因此,这样
也可以:
然而,如果宏定义被插在 include 指令中,它们直到 FreeMarker 执行 include 指
令时参会可用。
例如:没有参数的宏:
输出:
<#-- call the macro; the macro variable is already created: -->
<@test/>

<#-- create the macro variable: -->
<#macro test>
Test text
</#macro>
<#macro test>
Test text
</#macro>
<#-- call the macro: -->
<@test/>
示例:有参数的宏:
输出:
示例:有参数和默认值参数的宏:
输出:
示例:一个复杂的宏。
输出:
Test text
<#macro test foo bar baaz>
Test text, and the params: ${foo}, ${bar}, ${baaz}
</#macro>
<#-- call the macro: -->
<@test foo=“a” bar=“b” baaz=55-2/>
Test text, and the params: a, b, 23
<#macro test foo bar=“Bar” baaz=-1>
Test text, and the params: ${foo}, ${bar}, ${baaz}
</#macro>
<@test foo=“a” bar=“b” baaz=5
5-2/>
<@test foo=“a” bar=“b”/>
<@test foo=“a” baaz=5*5-2/>
<@test foo=“a”/>
Test text, and the params: a, b, 23
Test text, and the params: a, b, -1
Test text, and the params: a, Bar, 23
Test text, and the params: a, Bar, -1
<#macro list title items>

${title?cap_first}:

    <#list items as x>
  • ${x?cap_first}

There was no specific handler for node y
<#-- 假设 nodeWithNameX?node_name 是"x" -->
<#visit nodeWithNameX>
<#-- 假设 nodeWithNameY?node_type 是"foo" -->
<#visit nodeWithNameY>
<#macro x>
Handling node x
</#macro>
<#macro @foo>
There was no specific handler for node ${node?node_name}
</#macro>
<#recurse someNode using someLib>
<#list someNode?children as child><#visit child using
someLib></#list>
<#list .node?children as child><#visit child></#list>
2.22.2.3 fallback 指令
正如前面所学的,在 visit 指令的文档中,自定义指令控制的节点也许在多个 FTL 命
名空间中被搜索。fallback 指令可以被用在自定义指令中被调用处理节点。它指挥
FreeMarker 在更多的命名空间(也就是,在当前调用列表中自定义指令命名空间之后的命名
空间)中来继续搜索自定义指令。如果节点处理器被发现,那么就被调用,否则 fallback
不会做任何事情。
这个指令的典型用法是在处理程序库之上写定制层,有时传递控制到定制的库中:
<#import “/lib/docbook.ftl” as docbook>
<#-- 我们使用 docbook 类库,但是我们覆盖一些这个命名空间中的处理器–>
<#visit document using [.namespace, docbook]>
<#-- 覆盖"programlisting"处理器,但是要注意它的"role"属性是"java"
–>
<#macro programlisting>
<#if .node.@role[0]!"" == “java”>
<#-- 在这里做一些特殊的事情… -->

<#else>
<#-- 仅仅使用原来的(覆盖的)处理器 -->
<#fallback>
</#if>
</#macro>
第三章 特殊变量参考文档
特殊变量是由 FreeMarker 引擎自己定义的变量。要访问它们,你可以使
用.variable_name 语法。比如,你不能仅仅写 version,而必须写.version。
支持的特殊变量有:
 data_model:你可以使用来直接访问数据模型的哈希表。也就是,你使用
global 指令定义在这里不可见的的变量。
 error(从 FreeMarker 2.3.1 版本开始可用):这个变量在 recover 指令体中可
以访问,它存储了我们恢复错的错误信息。
 globals:你可以使用来访问全局可访问的变量的哈希表:数据模型和由
global 指令创建的变量。注意用 assign 或 macro 创建的变量不是全局的。
因此当你使用 globals 时你不能隐藏变量。
 language:返回当前本地设置的语言部分的值。比如.locale 是 en_US,
那么.lang 是 en。
 locale:返回当前本地设置的值。这是一个字符串,比如 en_US。要获取关于
本地化字符串值的更多内容,请参考 setting 指令。
 locales:你可以访问本地化变量的哈希表(由 local 指令创建的变量,还有
宏的参数)。
 main:你可以用来访问主命名空间的哈希表。注意像数据模型中的全局变量通过
这个哈希表是不可见的。
 namespace:你可以用来访问当前命名空间的哈希表。注意像数据模型中的全
局变量通过这个哈希表是不可见的。
 node(由于历史原因重命名为 current_node): 你可以用访问者模式(也
就是用 visit,recurse 等指令)处理的当前节点。而且,当你使用 FreeMarker
XML 的 Ant 任务时,它初始存储根节点。
 now : 返 回 当 前 的 时 间 日 期 。 使 用 示 例 : " Page generated:
${.now}",“Today is ${.now?date}”,“The current time is
${.now?time}”。
 output_encoding(从 FreeMarker 2.3.1 版本开始可用):返回当前输出字符
集的名称。如果框架封装 FreeMarker 却没有为 FreeMarker 指定输出字符集时这个
特殊变量是不存在的。(程序员可以阅读关于字符集问题的更多内容,在:程序开
发指南/其他/字符集问题部分。)
 template_name:当前模板的名称(从 FreeMarker 2.3.14 版本开始可用)。
 url_escaping_charset(从 FreeMarker 2.3.1 版本开始可用):如果存在,
它存储了应该用于 URL 转义的字符集的名称。如果这个变量不存在就意味着没有
人指定 URL 编码应该使用什么样的字符集。这种情况下,url 内建函数使用特殊
变量 output_encoding 指定的字符集来进行 URL 编码。处理机制和它是相同
的。(程序员可以阅读关于字符集问题的更多内容,在:程序开发指南/其他/字符
集问题部分。)
 vars:表达式.vars.foo 返回和表达式 foo 相同的变量。出于某些原因你不
得不使用方括号语法时这是有用的,因为它只对哈希表子变量有用,所以你需要一
个人工的父哈希表。比如,要读取有特殊名称的顶层变量可能会把 FreeMarker 弄
糊 涂 , 你 可以 写 .vars[“A strange name!”] 。 或者 , 使 用和 变 量
varName 给定的动态名称访问顶层变量你可以写.vars[varName]。注意这
个哈希表由.vars 返回,并不支持?keys 和?values。
 version:返回 FreeMarker 版本号的字符串形式,比如 2.2.8。这可以用来检
查你的应用程序使用的是哪个版本的 FreeMarker,但是要注意这个特殊变量在
2.3-final 或 2.2.8 版本之前不存在。非最终发行版本号包含缩写形式的“preview”,
是“pre”(比如 2.3pre6),或缩写形式的“release candidate”,是“rc”。
第四章 FTL 中的保留名称
下面的这些名称不能在非方括号语法中被用作顶层变量(比如 vars[“in”]),因为
这是 FTL 中的关键字。
 true:布尔值“true”
 false:布尔值“false”
 gt:比较运算符“大于”
 gte:比较运算符“大于或等于”
 lt:比较运算符“小于”
 lte:比较运算符“小于或等于”
 as:由少数指令使用
 in:由少数指令使用
 using:由少数指令使用
第五章 废弃的 FTL 结构
5.1 废弃的指令列表
下面这些指令是废弃的,但是仍然可以运行:
 call:使用自定义指令来代替调用
 comment:这是<#–…-->的老式格式。在<#comment>和</#comment>
之间的任何东西都会被忽略。
 foreach:它是 list 指令的代名词,有着轻微不同的参数语法。它的语法结
构是<#foreach item in sequence>,和<#list sequence as item>
是相同的。
 transform:使用自定义指令来代替调用
下面这些指令不在可以运行:
 遗留的 function:起初 function 是被用作定义宏,由 macro 指令的支持,
它就被废弃了。对于FreeMarker 2.3版本来说,这个指令由不同的意义而再次引入:
它被用来定义方法。
5.2 废弃的内建函数列表
下面这些内建函数是被废弃的,但是仍可以运行:
default:由默认值运算符的引入,它被废弃了。exp1?default(exp2)和
exp1!exp2 是相同的,(exp1)?default(exp2)和(exp1)!exp2.是相同的。唯
一的不同是在 FreeMarker 2.4 版本之前,内建函数 default 通常算作是 exp2,而默认
值运算符仅仅当默认值真的需要时才算。从 FreeMarker 2.4 版本之后,内建函数 default
被改进了,和默认值运算符的行为非常像了。
exists:由空值测试运算符的引入,它被废弃了。exp1?exists 和 exp1??是
一样的,(exp1)?exists 和(exp1)??也是一样的。
if_exists:由默认值运算符的引入,它被废弃了。exp1?if_exists 和 exp1!
相似,(exp1)?if_exists 和(exp1)!相似。不同之处在于,用 if_exists 的默
认值不仅仅同时是空字符串,空序列和空哈希表,而且布尔值 false 和不做任何事情的变
换,还有忽略所有参数。
web_safe:和 html 相同。
5.3 老式的 macro 和 call 指令
5.3.1 概要
<#macro name(argName1, argName2, … argNameN)>

</#macro>
<#call name(argValue1, argValue2, … argValueN)>
这里:
 name:宏的名称(不是表达式)
 argName1,argName2 等:存储参数值局部变量的名称(不是表达式)
 argValue1,argValue2 等:表达式,参数的值
5.3.2 描述
注意:
这是 FreeMarker 2.1 版本的文档中宏还有宏它相关的指令。这仍然可以用,但是已经被
废弃了。你也许想阅读 FreeMarker2.2+版本的参考:就是指令参考中的 macro,return 部分
和用户自定义指令部分。
宏是关联名称的模板段。你可以在你的模板中的很多位置使用命名的代码段,所以它可
以在重复的任务中帮助你。宏可以有参数,这会在你调用它的时候影响生成的输出。
你可以使用 macro 指令来定义宏,之后你可以在整个模板中定义宏。macro 指令本
身不往输出中写任何东西,它只是用来定义宏。例如这会定义一个称为 warning 的宏:
无论何时你使用 call 指令来调用这个宏时,宏定义体(在宏的开始标签和结束标签
之间的部分)将会被处理。比如这个调用了名为 warning 的宏:
作为 call 指令参数传递的参数将会在宏定义体中可以作为局部变量来访问。
当你调用一个宏,你必须指定和在宏定义时参数数量相同的参数。比如如果这个宏这么
来定义:
那么这些是合法的调用:
<#macro warning(message)>

Warning!

${message}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!