FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。
选择freemarker的原因:
- 性能。velocity应该是最好的,其次是jsp,普通的页面freemarker性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker的性能比使用tag和el的jsp好。
- 宏定义比jsp tag方便
- 内置大量常用功能。比如html过滤,日期金额格式化等等,使用非常方便
- 支持jsp标签
- 可以实现严格的mvc分离
FreeMarker模板文件主要由如下4个部分组成:
- 文本:直接输出的部分。
- 注释:
<#-- ... —>
格式部分,不会输出。 - 插值:即
${…}
或#{…}
格式的部分,将使用数据模型中的部分替代输出。 - FTL指令:FreeMarker指定,和HTML标记类似,名字前加
#
予以区分,不会输出。
例子
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<#-- 注释部分 -->
<#-- 下面使用插值 -->
<h1>Welcome ${user}!</h1>
<p>We have these animals:
<ul>
<#-- 使用FTL指令 -->
<#list animals as being>
<li>${being.name} for ${being.price} Euros
</#list>
</ul>
</body>
</html>
一、插值规则
FreeMarker的插值有如下两种类型:
- 通用插值
${expr}
- 数字格式化插值:
#{expr}
或#{expr;format}
1. 通用插值
- 输出
${book.name}
空值判断:
${book.name?if_exists}
${book.name?default(‘xxx’)} //默认值xxx
${book.name!"xxx"} //默认值xxx
- 日期格式:
${book.date?string('yyyy-MM-dd')}
数字格式:
${book?string.number} //20
${book?string.currency} //$20.00
${book?string.percent} //20%
2. 数字格式化插值
数字格式化插值可采用#{expr;format}
形式来格式化数字。
其中format
可以是:
mX
:小数部分最小X位MX
:小数部分最大X位
示例:
<#assign x=2.582/>
<#assign y=4/>
#{x; M2}
#{y; M2}
#{x; m2}
#{y; m2}
#{x; m1M2}
#{x; m1M2}
输出:
2.58
4
2.58
4.00
2.58
2.58
二、FTL指令规则
在FreeMarker中,使用FTL标签来使用指令,FreeMarker有3种FTL标签,这和HTML标签是完全类似的.
- 开始标签:
<#directivename parameter>
- 结束标签:
</#directivename>
- 空标签:
<#directivename parameter/>
1. 遍历List集合
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as item>
${item}
</#list>
这里会逐个输出星期几。
此外,迭代集合对象时,还包含两个特殊的循环变量:
item_index
:当前变量的索引值item_has_next
:是否存在下一个对象
也可以使用<#break>
指令跳出迭代。
2. 遍历map集合
{"语文":78, "数学":80}
Map对象的key和value都是表达式,但是key必须是字符串。
<#list map?keys as key>
${key}=${map[key]}<br/>
</#list>
3. 逻辑判断
if 判断
判断条件,当condition的判断结果为false
(布尔值)时,在<#if condition>
和</#if>
标签之间的内容将会被略过。
<#if animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
<#else>
Pythons are not cheaper than elephants today.
</#if>
需要注意的是比较运算符两边的类型必须相同,不能一个是 数字,一个是字符串,否则将会报错。
Switch 判断
<#switch value>
<#case refValue1>
...
<#break>
<#case refValue2>
...
<#break>
<#case refValueN>
...
<#break>
<#default>
...
</#switch>
4. 空值处理
FreeMarker对空值的处理非常严格,FreeMarker的变量必须有值,没有被赋值的变量就会抛出异常,因为FreeMarker未赋值的变量强制出错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误。这里所说的空值,实际上也包括那些并不存在的变量,对于一个Java的null
值而言,我们认为这个变量是存在的,只是它的值为null
,但对于FreeMarker模板而言,它无法理解null
值,null
值和不存在的变量完全相同。
为了处理缺失变量,FreeMarker提供了两个运算符:
!
:指定缺失变量的默认值??
:判断某个变量是否存在
指定缺失变量的默认值
!
运算符的用法有如下两种:
variable!
variable!defaultValue
第一种用法不给缺失的变量指定默认值,表明默认值是空字符串、长度为0的集合或者长度为0的Map对象。
另外,使用!
指定默认值时,并不要求默认值的类型和变量类型相同。
Demo:
<h1>Welcome ${user!"Anonymous"}!</h1>
<#assign user = "Doe Joe">
<h1>Welcome ${user!"Anonymous"}!</h1>
输出:
<h1>Welcome Anonymous!</h1>
<h1>Welcome Doe Joe!</h1>
判断某个变量是否存在
使用??
运算符非常简单,它总是返回一个布尔值,用法为:variable??
,如果该变量存在,返回true
,否则返回false
但是需要注意的是,我们在使用freemarker获取后端回填数据时,必须使用??
来判断是否存在,再进行使用。或者通过default来赋予默认值,否则一旦数据不存在,就会出现前端报错完全崩溃的情况。
Demo:
<#if mouse??>
Mouse found.
<#else>
No mouse found.
</#if>
实际使用示例:
<#if !productList??>
<div class="n-result">
<h3>暂无内容!</h3>
</div>
<#else>
<div class="n-plist">
<ul class="f-cb" id="plist">
<#list productList as x>
<#if !x.isBuy>
<li id="p-${x.id}">
<a href="/show?id=${x.id}" class="link">
<div class="img"><img src="${x.image}" alt="${x.title}"></div>
<h3>${x.title}</h3>
<div class="price"><span class="v-unit">¥</span><span class="v-value">${x.price}</span></div>
</a>
</li>
</#if>
</#list>
</ul>
</div>
</#if>
5. 变量的声明
plain变量
它能从模板中的任何位置来访问,或者从使用include指令引入的模板访问。可以使用assign
和macro
指令来创建或替换这些变量。
<#assign num=0/>
<#assign x="Hello ${user}!"/>
局部变量
只能在#function
或#macro
定义体中定义&有效,使用local指令创建和替换。
局部变量会隐藏同名的plain变量。
循环变量
只能存在于指令的嵌套内容,由指令(如list)自动创建;宏的参数是局部变量,而不是循环变量。
循环变量会隐藏同名的局部变量和plain变量,且内部循环变量会隐藏外部循环变量。
如果想要跳出作用域,直接使用数据模型中的变量,使用globals关键字即可:
<#assign user = "Joe">
${user}
${.globals.user}
输出:
Joe
Doe
6. 运算符的优先级
FreeMarker中的运算符优先级如下(由高到低排列):
- 一元运算符:
!
- 内建函数:
?
- 乘除法:
*
,/
,%
- 加减法:
-
,+
- 比较:
>
,<
,>=
,<=
- 相等:
==
,=
,!=
- 逻辑与:
&&
- 逻辑或:
||
- 数字范围:
..
7. include指令
include 指令的作用类似于 JSP 的包含指令,用于包含指定页。include指令的语法格式如下:
<#include filename [options]>
示例:
<#include "/header.html">
<#include "/footer.ftl">
8. import 指令及命名空间
import
指令类似于 java 里的 import,它导入文件,然后就可以在当前文件里使用被导入文件里的宏组件。
我们通过assign和macro创建的变量的集合就是命名空间。命名空间的作用在各种编程语言中都已见识,这里假设我们定义了自己的macro和assign位于lib/mylib.ftl中。
<#macro copyright date>
<p>Copyright (C) ${date} Julia Smith. All rights reserved.</p>
</#macro>
<#assign mail = "jsmith@acme.com">
通过import来引用:
<#import "/libs/mylib.ftl" as my>
<#-- 被称为"my"的哈希表就会是那个"大门" -->
<@my.copyright date="1999-2002"/>
${my.mail}
输出:
<p>Copyright (C) ${date} Julia Smith. All rights reserved.</p>
jsmith@acme.com
覆盖变量
引入命名空间后替换变量需要在assign的基础上加上in关键字:
<#import "/lib/mylib.ftl" as my>
${my.mail}
<#assign mail = "jsmith@other.com" in my>
${my.mail}
输出:
jsmith@acme.com
jsmith@other.com
9. noparse指令
noparse
指令指定 FreeMarker 不处理该指定里包含的内容,该指令的语法格式如下:
<#noparse>...</#noparse>
示例:
<#noparse>
<#list animals as animal>
<li>${animal.name} for ${animal.price} Euros</li>
</#list>
</#noparse>
输出:
<#list animals as animal>
<li>${animal.name} for ${animal.price} Euros</li>
</#list>
10. setting指令
该指令用于设置FreeMarker的运行环境,该指令的语法格式如下:
<#setting name=value>
在这个格式中,name
的取值范围包含如下几个:
locale
:该选项指定该模板所用的国家/语言选项number_format
:指定格式化输出数字的格式boolean_format
:指定两个布尔值的语法格式,默认值是true,falsedate_format
,time_format
,datetime_format
:指定格式化输出日期的格式time_zone
:设置格式化输出日期时所使用的时区
三、内建函数
内建函数以?
形式提供变量的不同形式或者其他信息。多个内建函数可以通过?
连接来使用。
字符串常用的内建函数:
html
:将字符串中的所有特殊 HTML字符进行转义。例如:<
替换成<
cap_first
:将字符串的首字母大写lower_case
:将字符串转换为小写形式upper_case
:将字符串转换为大写形式trim
:去掉字符串首尾的空格
集合常用的内建函数:
size
:集合中元素的个数chunk(size)
:分成几个一组
数字常用的内建函数:
- int:将数字转换为整数。方式为直接去除小数部分。
示例:
${test?html}
${test?lower_case?html}
${3 * 2 + 2} <#-- 8 -->
${3 * (2 + 2)} <#-- 12 -->
${3 * ((2 + 2) * (1 / 2))} <#-- 6 -->
${"green " + "mouse"?upper_case} <#-- green MOUSE -->
${("green " + "mouse")?upper_case} <#-- GREEN MOUSE -->
number_to_datetime
一个转换时间格式的内建函数。
${timestamp?number_to_datetime?string["MM-dd HH:mm"]}
eval
将字符串作为ftl模板输出,就和javascript原生的eval有点类似。
示例:
${(col['name']?eval)}
四、宏
宏是在模板中使用macro指令定义,宏是和某个变量关联的模板片断,以便在模板中通过用户定义指令使用该变量,有人说用freemarker,但没有用到它的宏(macro),就等于没有真正用过freemarker。说的就是宏是freemarker的一大特色。
其基本语法如下:
<#macro name param1 param2 ... paramN>
...
<#nested loopvar1, loopvar2, ..., loopvarN>
...
<#return>
...
</#macro>
在上面的格式片段中,包含了如下几个部分:
name
:name
属性指定的是该自定义指令的名字,使用自定义指令时可以传入多个参数paramX
:该属性就是指定使用自定义指令时报参数,使用该自定义指令时,必须为这些参数传入值nested
指令:nested
标签输出使用自定义指令时的中间部分nested
指令中的循环变量:这此循环变量将由macro
定义部分指定,传给使用标签的模板return
指令:该指令可用于随时结束该自定义指令.
需要注意的是:调用宏时,与使用FreeMarker的其他指令类似,只是使用@
替代FTL标记中的#
。
1. macro定义模板,然后调用直接显示
<#macro greet>
<font size="+2">Hello World!</font>
</#macro>
使用:
<@greet/>
<@greet></@greet>
输出:
<font size="+2">Hello World!</font>
<font size="+2">Hello World!</font>
2. 在macro指令中可以在宏变量之后定义参数
<#macro greet person>
<font size="+2">Hello ${person}!</font>
</#macro>
使用:
<@greet person="Doe"></@greet>
<@greet person="Joe"/>
<@greet "Foo"/>
输出:
<font size="+2">Hello Doe!</font>
<font size="+2">Hello Joe!</font>
<font size="+2">Hello Foo!</font>
3. macro 定义多个参数
macro可以有多个参数,参数的次序是无关的(如果使用 Postional style 调用则需要按顺序),在macro指令中只能使用定义的参数,并且必须对所有参数赋值,可以在定义参数时指定缺省值:
<#macro greet person color="black">
<font size="+2" color="${color}">Hello ${person}!</font>
</#macro>
使用:
<#-- Named style -->
<@greet person="Doe"></@greet>
<@greet person="Doe"/>
<@greet person="Doe" color= "green"/>
<#-- Postional style -->
<@greet "Doe"/>
<@greet "Doe", "green"/>
输出:
<font size="+2" color="black">Hello Doe!</font>
<font size="+2" color="black">Hello Doe!</font>
<font size="+2" color="green">Hello Doe!</font>
<font size="+2" color="black">Hello Doe!</font>
<font size="+2" color="green">Hello Doe!</font>
4. 自定义指令嵌套内容 <#nested>
<#macro border>
<table border=4 cellspacing=0 cellpadding=4>
<tr>
<td>
<#nested>
</td>
</tr>
</table>
</#macro>
使用:
<@border>The bordered text</@border>
输出:
<table border=4 cellspacing=0 cellpadding=4>
<tr>
<td>
The bordered text
</td>
</tr>
</table>
<#nested>
就相当于占位符
<#nested>
指令可以被多次调用:
<#macro do_thrice>
<#nested>
<#nested>
<#nested>
</#macro>
使用:
<@do_thrice>Anything.</@do_thrice>
输出:
Anything.
Anything.
Anything.
5. 局部变量对嵌套内容不可见
<#macro repeat count>
<#local y = "test">
<#list 1..count as x>
${y} ${count}/${x}: <#nested>
</#list>
</#macro>
<@repeat count=3>${y?default("?")} ${x?default("?")} ${count?default("?")}</@repeat>
输出:
test 3/1: ? ? ?
test 3/2: ? ? ?
test 3/3: ? ? ?
其中嵌套内容中的y
,x
,count
都是没定义的,所以取不到值。
6. 宏定义中使用循环变量
nested指令也可以有循环变量(循环变量的含义见下节),调用宏的时候在宏指令的参数后面,分号隔开依次列出循环变量的名字,格式如下:
<@macro_name paramter list; loop variable list[,]>
示例:
<#macro repeat count>
<#list 1..count as x>
<#nested x, x/2, x==count>
</#list>
</#macro>
<@repeat count=4 ; c, halfc, last>
${c}. ${halfc}<#if last> Last!</#if>
</@repeat>
count
是宏的参数,c
,halfc
,last
则为循环变量,与宏中<#nested>
里定义的x
, x/2
, x==count
一一对应。
输出:
1, 0.5
2, 1
3, 1.5
4, 2 Last!
引用
循环变量和宏标记指定的不同不会有问题,如果调用时少指定了循环变量,那么多余的值不可见。调用时多指定了循环变量,多余的循环变量不会被创建:
<@repeat count=4 ; c, halfc, last>
${c}. ${halfc}<#if last> Last!</#if>
</@repeat>
<@repeat count=4 ; c, halfc>
${c}. ${halfc}
</@repeat>
<@repeat count=4>
Just repeat it...
</@repeat>
转载
内容非原创,转载自:
- freemarker(一) - 简书 By 搁浅的双鱼
- freemarker(二) - 简书 By 搁浅的双鱼
- freemarker(三)宏 - 简书 By 搁浅的双鱼
- FreeMarker模板基本语法
参考阅读:
厉害,学习了,快速入门
预祝你新年大吉吧\#(装大款)
默默膜拜技术帝
看来是有一定的研究的。@(吐舌)
居然有目录树了好酷~#(委屈的吃手手)#
又挖苦我\#(投降)
真的好棒啊,我也打算加一个~#(关爱制杖)#