一、什么是Freemarker
FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本。模板文件即ftl文件,数据对象是java对象。
模板 + 数据模型 = 输出
二、Freemarker的数据模型
1、顶层数据模型
数据模型是树状结构的,顶层默认为root节点
(root)
|+- animals
| |+- mouse
| | |+- size = "small"
| | |+- price = 50
| |+- elephant
2、Hashes类型
这种类型可以通过名字来查找。顶层节点就是这种类型。例如可以通过animals.mouse来访问mouse。
3、sequences 类型
它们像哈希表那样存储子变量,但是子变量没有名字,它们只是列表中的项。
(root)
| +- animals
| |+- (1st)
| | |+- name = "mouse"
| | |+- size = "small"
| | |+- price = 50
| |+- (2nd)
要访问序列的子变量,可以使用方括号形式的数字索引下标。 索引下标从0开始
4、scalars 类型
包括:
- 字符串:就是文本,也就是任意的字符序列。
-
数字:这是数值类型,就像上面的
price
。 在FreeMarker中,字符串"50"
和数字50
是两种完全不同的东西。前者是两个字符的序列 (这恰好是人们可以读的一个数字),而后者则是可以在数学运算中直接被使用的数值。 -
日期/时间: 可以是日期-时间格式(存储某一天的日期和时间), 或者是日期(只有日期,没有时间),或者是时间(只有时间,没有日期)。
-
布尔值:对应着对/错(是/否,开/关等值)类似的值。 比如动物可以有一个
protected
(受保护的,译者注) 的子变量, 该变量存储这个动物是否被保护起来的值。
三、Freemarker的模板
-
${...}
: FreeMarker将会输出真实的值来替换大括号内的表达式,这样的表达式被称为 interpolation(插值,译者注)。 -
FTL 标签 (FreeMarker模板的语言标签): FTL标签和HTML标签有一些相似之处,但是它们是FreeMarker的指令,是不会在输出中打印的。 这些标签的名字以
#
开头。(用户自定义的FTL标签则需要使用@
来代替#
。) -
注释: 注释和HTML的注释也很相似, 但是它们使用
<#--
and-->
来标识。 不像HTML注释那样,FTL注释不会出现在输出中(不出现在访问者的页面中), 因为 FreeMarker会跳过它们。 - 其他任何不是FTL标签,插值或注释的内容将被视为静态文本, 这些东西不会被FreeMarker所解析;会被按照原样输出出来。
if 指令
<#if animals.python.price < animals.elephant.price> Pythons are cheaper than elephants today. <#elseif animals.elephant.price < animals.python.price> Elephants are cheaper than pythons today. <#else> Elephants and pythons cost the same today. </#if>
通常来讲,如果 condition
是false(布尔值),那么介于 <#if condition>
和 </#if>
标签中的内容会被略过。
list 指令
<p>Fruits: <#list misc.fruits as fruit>${fruit}<#sep>, </#list>
使用分隔符 <#sep>
include 指令
使用 include
指令, 我们可以在模板中插入其他文件的内容。
事实上,指令有两种类型: 预定义指令 和 用户自定义指令。 对于用户自定义的指令使用 @
来代替 #
,比如,<@mydirectiveparameters>...</@mydirective>
。 更深的区别在于如果指令没有嵌套内容,那么必须这么使用 <@mydirective parameters />自定义指令也可以通过代码自定义
自定义指令示例:
<#macro printAnimal animal>
<animal name="${animal.name}" size="${animal.size}">
<#nested>
</#macro>
<#list animals.getMap()?values as animal>
<@printAnimal animal>
<!-- xml注释内容 -->
</@printAnimal>
</#list>
四、Freemarker的内建函数
内建函数很像子变量(如果了解Java术语的话,也可以说像方法), 它们并不是数据模型中的东西,是 FreeMarker 在数值上添加的。 为了清晰子变量是哪部分,使用 ?
(问号)代替 .
(点)来访问它们。常用内建函数的示例:
-
user?html
给出user
的HTML转义版本, 比如&
会由&
来代替。 -
user?upper_case
给出user
值的大写版本 (比如 "JOHN DOE" 来替代 "John Doe") -
animal.name?cap_first
给出animal.name
的首字母大写版本(比如 "Mouse" 来替代 "mouse") -
user?length
给出user
值中 字符的数量(对于 "John Doe" 来说就是8) -
animals?size
给出animals
序列中 项目 的个数(我们示例数据模型中是3个) -
如果在
<#list animals as animal>
和对应的</#list>
标签中:-
animal?index
给出了在animals
中基于0开始的animal
的索引值 -
animal?counter
也像index
, 但是给出的是基于1的索引值 -
animal?item_parity
基于当前计数的奇偶性,给出字符串 "odd" 或 "even"。在给不同行着色时非常有用,比如在<td class="${animal?item_parity}Row">
中。
-
一些内建函数需要参数来指定行为,比如:
-
animal.protected?string("Y", "N")
基于animal.protected
的布尔值来返回字符串 "Y" 或 "N"。 -
animal?item_cycle('lightRow','darkRow')
是之前介绍的item_parity
更为常用的变体形式。 -
fruits?join(", ")
通过连接所有项,将列表转换为字符串, 在每个项之间插入参数分隔符(比如 "orange,banana") -
user?starts_with("J")
根据user
的首字母是否是 "J" 返回布尔值true或false。
内建函数应用可以链式操作,比如user?upper_case?html
会先转换用户名到大写形式,之后再进行HTML转义。(这就像可以链式使用 .
(点)一样)
处理不存在的变量
<h1>Welcome ${user!"visitor"}!</h1> 当user为空时,使用默认只visitor
<#if user??><h1>Welcome ${user}!</h1></#if> ??表示条件判断,相当于if(null!=user)