FreeMarker是一个用Java编写的模板引擎,主要用来生成HTML Web页面,特别是基于MVC模式的应用程序。虽然FreeMarker具有一些编程的能力,但不像PHP,通常由Java程序准备要显示的数据,由FreeMarker模板生成页面。 FreeMarker可以作为Web应用框架一个组件,但它与容器无关,在非Web应用程序环境也能工作的很好。 FreeMarker适合作为MVC的视图组件,还能在模板中使用JSP标记库。
上面的例子中,在简单的HTML中加入了一些由${…}包围的特定FreeMarker的指令,这个文件就称为模板了。而user、latestProduct.url和latestProduct.name来自于数据模型,由Java程序提供,模板设计者就不用关心数据从哪来的。
FreeMarker模板中可以包括下面四种特定部分:
[b]一)文本[/b]:直接输出
[b]二)FTL标记(FreeMarker模板语言标记):[/b]类似于HTML标记,名字前加#(有些以@开始,用户自定义标记)予以区分,不会输出。
[b]1.字符串[/b]:使用单引号或双引号限定;如果包含特殊字符需要转义符:${"It's \"quoted\" andthis is a backslash: \\"}
有一类特殊的字符串:${r"C:\foo\bar"},输出结构为:C:\foo\bar,在引号前面加r被认为是纯文本。
[b]2.数字[/b]:直接输入,不需要引号。${08}, ${+8}, ${8.00} and ${8} 都是相同的
[b]3.布尔值[/b]:true和false,不使用引号
[b]4.Sequences(序列)[/b]
a.由逗号分隔的变量列表,由方括号限定,类似java中的一维数组:
例一:["winter", "spring", "summer", "autumn"]
例二:[2 + 2, [1, 2, 3, 4], "whatnot"] (可以嵌套)
例三:2..5,等同于[2, 3, 4, 5];5..2,等同于[5,4,3,2]。
注意方括号是不需要的。 (另外写法)
b.获取Sequence(序列)中元素-使用[startindex..endindex]
例如:seq中存储了"a", "b", "c", "d","e",
那么seq[1..2]包含了b和c两个值。
c.Sequences(序列)元素的遍历
<#list ["Joe", "Fred"] + ["Julia", "Kate"] as user>
${user}
</#list>
[b]5.Hashes(散列)[/b]
由逗号分隔的键-值列表,由大括号限定,键和值之间用冒号分隔:{"name":"green mouse", "price":150},键和值都是表达式,但是键必须是字符串。
获取变量-${variable},变量名只能是字母、数字、下划线、$、@和#的组合,且不能以数字开头。可以使用.variablename语法访问FreeMarker内置变量。 下列表达式是等价的:
book.author.name
book["author"].name
book.author.["name"]
book["author"]["name"]
[b]6.字符串操作 [/b]
{"Hello ${user}!"} <==> ${"Hello " + user + "!"}
${"${user}${user}${user}${user}"} <==> ${user + user + user + user}
${…}只能在文本中使用,下面是错误的代码:
<#if ${isBig}>Wow!</#if>
<#if "${isBig}">Wow!</#if> //此处的代码也是错误的,因为if指令需要的是boolean,实际的却是个字符串
子字符串的操作,
<#assign user="Big Joe">
${user[0]}${user[4]} <==> BJ
${user[1..4]} <==> ig J
注意: 操作符两边必须是数字;使用"+"时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串。
[b]7.使用内建的指令int获得整数部分:[/b]
${(x/2)?int}
${1.1?int}
${1.999?int}
${-1.1?int}
${-1.999?int}
[b]8.比较操作符-<#if expression>...</#if> [/b]
1.)使用=(或==,完全相等)测试两个值是否相等,使用!= 测试两个值是否不相等
2.)=和!=两边必须是相同类型的值,否则会产生错误,例如<#if 1 = "1">会引起错误
3.)Freemarker是精确比较,所以"x"、"x "和"X"是不相等的
4.)对数字和日期可以使用<、<=、>和>=,但不能用于字符串
5.)由于Freemarker会将>解释成FTL标记的结束字符,所以对于>和>=可以使用括号来避免这种情况,例如<#if (x > y)>,另一种替代的方法是,使用lt、lte、gt和gte来替代<、<=、>和>=
6.)逻辑操作符-&&(and)、||(or)、!(not),只能用于布尔值,否则会产生错误
<#if x < 12 && color = "green">
We have less than 12 things, and they are green.
</#if>
<#if !hot> <#-- here hot must be a boolean -->
It's not hot.
</#if>
[b]9.内置函数-用法类似访问hash(散列)的子变量,只是使用"?"替代".",[/b]
例如:user?upper_case
下面列出常用的一些函数:
对于字符串
[b]html[/b]-对字符串进行HTML编码
[b]cap_first[/b]-使字符串第一个字母大写
[b]lower_case[/b]-将字符串转换成小写
[b]trim[/b]-去掉字符串前后的空白字符
对于Sequences(序列)
size-获得序列中元素的数目
对于数字
int-取得数字的整数部分(如-1.9?int的结果是-1)
[b]10.方法的调用 [/b]
${repeat("What", 3)}
${repeat(repeat("x", 2), 3) + repeat("What", 4)?upper_case}
结果:
WhatWhatWhat
xxxxxxWHATWHATWHATWHAT
[b]11.操作符优先顺序[/b]
后缀 [subvarName] [subStringRange] . (methodParams)
一元 +expr、-expr、!
内建 ?
乘法 *、 / 、%
加法 +、-
关系 <、>、<=、>=(lt、lte、gt、gte)
相等 =、!=
逻辑 &&
逻辑 ||
数字范围 ..
[b]12.一些常用控制语句[/b]:
[b]1)if, else, elseif[/b]
[b]2)switch, case, default, break[/b]
<#switch value>
[b]3) list, break[/b]
[b]4)include[/b]
[b]5)import[/b]
Note: that it is possible to automatically do the commonly used imports for all templates, with the "auto imports" setting of Configuration.
[b]6)noparse[/b]
[b]7)compress[/b]
[b]8)escape, noescape[/b]
[b]9)assign[/b]
[b]10)global[/b]
[b]11)local[/b]
note:it is similar to assign directive, but it creates or replaces local variables. This works in macro definition bodies only.
[b]12)setting[/b]
note:The supported settings are:locale,number_format,boolean_format,
date_format, time_format, datetime_format,time_zone,url_escaping_charset,classic_compatible
[b]13) (<@...>)[/b]
[b]14)macro, nested, return[/b]
[b]15)function, return[/b]
[b]16).flush
17)stop
18)ftl
19)t, lt, rt[/b]
[b]20)nt
21)attempt, recover[/b]
[b]22)visit, recurse, fallback[/b]
[b]三. Interpolation:由${...}或#{...}两种类型,输出计算值,可以自定义输出的格式 [/b]
1)string的操作:
[b]substring
cap_first
uncap_first
capitalize
chop_linebreak
date, time, datetime
ends_with
html
groups
index_of
j_string
js_string
last_index_of
length
lower_case
left_pad
right_pad
contains
matches
number
replace
rtf
url
split
starts_with
string (when used with a string value)
trim
upper_case
word_list
xml[/b]
2)numbers的操作:
[b]c
string (when used with a numerical value)
round, floor, ceiling[/b]
3)dates的操作:
[b]string (when used with a date value)
date, time, datetime[/b]
4)booleans的操作:
[b]string (when used with a boolean value)[/b]
5)sequences的操作:
[b]first
last
seq_contains
seq_index_of
seq_last_index_of
reverse
size
sort
sort_by
chunk[/b]
6)hashes的操作:
[b]keys
values[/b]
7)nodes 的操作:
[b]children
parent
root
ancestors
node_name
node_type
node_namespace[/b]
[b]四.) 注释:<#--和--> [/b]
下面是一个常用的模板例子:
例一:
注意点:
1.) FreeMarker是区分大小写的;
2.) FTL标记不能位于另一个FTL标记内部,例如:<#if <#include 'foo'>='bar'>...</if>;
3.) ${…}只能在文本中使用;
4.) 多余的空白字符会在模板输出时去除;
5.) 如果使用的指令不存在,会产生一个错误消息。
例子2。
fmtag.tld文件:
例子3:
inedex.ftl 代码:
form.ftl代码
add.ftl代码内容
common.ftl的 代码
FreeMarker内部运行的某些原理:
个人感觉有兴趣和 时间的人可以去研究下《Programmer Guide》部分(原文档共有四个部分)该部分主要是介绍FreeMarker内部运行的某些原理
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome ${user}!</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>
上面的例子中,在简单的HTML中加入了一些由${…}包围的特定FreeMarker的指令,这个文件就称为模板了。而user、latestProduct.url和latestProduct.name来自于数据模型,由Java程序提供,模板设计者就不用关心数据从哪来的。
FreeMarker模板中可以包括下面四种特定部分:
[b]一)文本[/b]:直接输出
[b]二)FTL标记(FreeMarker模板语言标记):[/b]类似于HTML标记,名字前加#(有些以@开始,用户自定义标记)予以区分,不会输出。
[b]1.字符串[/b]:使用单引号或双引号限定;如果包含特殊字符需要转义符:${"It's \"quoted\" andthis is a backslash: \\"}
有一类特殊的字符串:${r"C:\foo\bar"},输出结构为:C:\foo\bar,在引号前面加r被认为是纯文本。
[b]2.数字[/b]:直接输入,不需要引号。${08}, ${+8}, ${8.00} and ${8} 都是相同的
[b]3.布尔值[/b]:true和false,不使用引号
[b]4.Sequences(序列)[/b]
a.由逗号分隔的变量列表,由方括号限定,类似java中的一维数组:
例一:["winter", "spring", "summer", "autumn"]
例二:[2 + 2, [1, 2, 3, 4], "whatnot"] (可以嵌套)
例三:2..5,等同于[2, 3, 4, 5];5..2,等同于[5,4,3,2]。
注意方括号是不需要的。 (另外写法)
b.获取Sequence(序列)中元素-使用[startindex..endindex]
例如:seq中存储了"a", "b", "c", "d","e",
那么seq[1..2]包含了b和c两个值。
c.Sequences(序列)元素的遍历
<#list ["Joe", "Fred"] + ["Julia", "Kate"] as user>
${user}
</#list>
[b]5.Hashes(散列)[/b]
由逗号分隔的键-值列表,由大括号限定,键和值之间用冒号分隔:{"name":"green mouse", "price":150},键和值都是表达式,但是键必须是字符串。
获取变量-${variable},变量名只能是字母、数字、下划线、$、@和#的组合,且不能以数字开头。可以使用.variablename语法访问FreeMarker内置变量。 下列表达式是等价的:
book.author.name
book["author"].name
book.author.["name"]
book["author"]["name"]
[b]6.字符串操作 [/b]
{"Hello ${user}!"} <==> ${"Hello " + user + "!"}
${"${user}${user}${user}${user}"} <==> ${user + user + user + user}
${…}只能在文本中使用,下面是错误的代码:
<#if ${isBig}>Wow!</#if>
<#if "${isBig}">Wow!</#if> //此处的代码也是错误的,因为if指令需要的是boolean,实际的却是个字符串
子字符串的操作,
<#assign user="Big Joe">
${user[0]}${user[4]} <==> BJ
${user[1..4]} <==> ig J
注意: 操作符两边必须是数字;使用"+"时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串。
[b]7.使用内建的指令int获得整数部分:[/b]
${(x/2)?int}
${1.1?int}
${1.999?int}
${-1.1?int}
${-1.999?int}
[b]8.比较操作符-<#if expression>...</#if> [/b]
1.)使用=(或==,完全相等)测试两个值是否相等,使用!= 测试两个值是否不相等
2.)=和!=两边必须是相同类型的值,否则会产生错误,例如<#if 1 = "1">会引起错误
3.)Freemarker是精确比较,所以"x"、"x "和"X"是不相等的
4.)对数字和日期可以使用<、<=、>和>=,但不能用于字符串
5.)由于Freemarker会将>解释成FTL标记的结束字符,所以对于>和>=可以使用括号来避免这种情况,例如<#if (x > y)>,另一种替代的方法是,使用lt、lte、gt和gte来替代<、<=、>和>=
6.)逻辑操作符-&&(and)、||(or)、!(not),只能用于布尔值,否则会产生错误
<#if x < 12 && color = "green">
We have less than 12 things, and they are green.
</#if>
<#if !hot> <#-- here hot must be a boolean -->
It's not hot.
</#if>
[b]9.内置函数-用法类似访问hash(散列)的子变量,只是使用"?"替代".",[/b]
例如:user?upper_case
下面列出常用的一些函数:
对于字符串
[b]html[/b]-对字符串进行HTML编码
[b]cap_first[/b]-使字符串第一个字母大写
[b]lower_case[/b]-将字符串转换成小写
[b]trim[/b]-去掉字符串前后的空白字符
对于Sequences(序列)
size-获得序列中元素的数目
对于数字
int-取得数字的整数部分(如-1.9?int的结果是-1)
[b]10.方法的调用 [/b]
${repeat("What", 3)}
${repeat(repeat("x", 2), 3) + repeat("What", 4)?upper_case}
结果:
WhatWhatWhat
xxxxxxWHATWHATWHATWHAT
[b]11.操作符优先顺序[/b]
后缀 [subvarName] [subStringRange] . (methodParams)
一元 +expr、-expr、!
内建 ?
乘法 *、 / 、%
加法 +、-
关系 <、>、<=、>=(lt、lte、gt、gte)
相等 =、!=
逻辑 &&
逻辑 ||
数字范围 ..
[b]12.一些常用控制语句[/b]:
[b]1)if, else, elseif[/b]
<#if x == 1>
x is 1
<#if y == 1>
and y is 1 too
<#else>
but y is not
</#if>
<#else>
x is not 1
<#if y < 0>
and y is less than 0
</#if>
</#if>
[b]2)switch, case, default, break[/b]
<#switch value>
<#switch being.size>
<#case "small">
This will be processed if it is small
<#break>
<#case "medium">
This will be processed if it is medium
<#break>
<#case "large">
This will be processed if it is large
<#break>
<#default>
This will be processed if it is neither
</#switch>
[b]3) list, break[/b]
<#list sequence as item>
${item}
<#if item = "xx"><#break></#if>
</#list>
[b]4)include[/b]
<#include "/common/navbar.html" parse=false encoding="Shift_JIS">
[b]5)import[/b]
<#import "/libs/mylib.ftl" as my>
<@my.copyright date="1999-2002"/>
Note: that it is possible to automatically do the commonly used imports for all templates, with the "auto imports" setting of Configuration.
[b]6)noparse[/b]
<#noparse>
<#list animals as being>
<tr><td>${being.name}<td>${being.price} Euros
</#list>
</#noparse>
[b]7)compress[/b]
<#assign x = " moo \n\n ">
(<#compress>
1 2 3 4 5
${moo}
test only
I said, test only
</#compress>)
[b]8)escape, noescape[/b]
<#assign x = "<test>">
<#macro m1>
m1: ${x}
</#macro>
<#escape x as x?html>
<#macro m2>m2: ${x}</#macro>
${x}
<@m1/>
</#escape>
${x}
<@m2/>
<#escape x as x?html>
From: ${mailMessage.From}
Subject: ${mailMessage.Subject}
<#noescape>Message: ${mailMessage.htmlFormattedBody}</#noescape>
...
</#escape>
<#escape x as x?html>
Customer Name: ${customerName}
Items to ship:
<#escape x as itemCodeToNameMap[x]>
${itemCode1}
${itemCode2}
${itemCode3}
${itemCode4}
</#escape>
</#escape>
[b]9)assign[/b]
<#import "/mylib.ftl" as my>
<#assign
seasons = ["winter", "spring", "summer", "autumn"]
test = test + 1
bgColor="red" in my
>
<#macro myMacro>foo</#macro>
<#assign x>
<#list 1..3 as n>
${n} <@myMacro />
</#list>
</#assign>
Number of words: ${x?word_list?size}
${x}
<#assign x="Hello ${user}!">
[b]10)global[/b]
<#global name=value>
or
<#global name1=value1 name2=value2 ... nameN=valueN>
or
<#global name>
capture this
</#global>
[b]11)local[/b]
note:it is similar to assign directive, but it creates or replaces local variables. This works in macro definition bodies only.
<#local name=value>
or
<#local name1=value1 name2=value2 ... nameN=valueN>
or
<#local name>
capture this
</#local>
[b]12)setting[/b]
note:The supported settings are:locale,number_format,boolean_format,
date_format, time_format, datetime_format,time_zone,url_escaping_charset,classic_compatible
${1.2}
<#setting locale="en_US">
${1.2}
[b]13) (<@...>)[/b]
<@myRepeatMacro count=4 ; x, last>
${x}. Something... <#if last> This was the last!</#if>
</@myRepeatMacro>
[b]14)macro, nested, return[/b]
<#macro img src extra...>
<img src="/context${src?html}"
<#list extra?keys as attr>
${attr}="${extra[attr]?html}"
</#list>
>
</#macro>
<@img src="/images/test.png" width=100 height=50 alt="Test"/>
<#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>
<#macro test>
Test text
<#return>
Will not be printed.
</#macro>
<@test/>
[b]15)function, return[/b]
<#function avg nums...>
<#local sum = 0>
<#list nums as num>
<#local sum = sum + num>
</#list>
<#if nums?size != 0>
<#return sum / nums?size>
</#if>
</#function>
${avg(10, 20)}
${avg(10, 20, 30, 40)}
${avg()!"N/A"}
[b]16).flush
17)stop
18)ftl
19)t, lt, rt[/b]
[b]20)nt
21)attempt, recover[/b]
Primary content
<#attempt>
Optional content: ${thisMayFails}
<#recover>
Ops! The optional content is not available.
</#attempt>
Primary content continued
[b]22)visit, recurse, fallback[/b]
[b]三. Interpolation:由${...}或#{...}两种类型,输出计算值,可以自定义输出的格式 [/b]
1)string的操作:
[b]substring
cap_first
uncap_first
capitalize
chop_linebreak
date, time, datetime
ends_with
html
groups
index_of
j_string
js_string
last_index_of
length
lower_case
left_pad
right_pad
contains
matches
number
replace
rtf
url
split
starts_with
string (when used with a string value)
trim
upper_case
word_list
xml[/b]
2)numbers的操作:
[b]c
string (when used with a numerical value)
round, floor, ceiling[/b]
3)dates的操作:
[b]string (when used with a date value)
date, time, datetime[/b]
4)booleans的操作:
[b]string (when used with a boolean value)[/b]
5)sequences的操作:
[b]first
last
seq_contains
seq_index_of
seq_last_index_of
reverse
size
sort
sort_by
chunk[/b]
6)hashes的操作:
[b]keys
values[/b]
7)nodes 的操作:
[b]children
parent
root
ancestors
node_name
node_type
node_namespace[/b]
[b]四.) 注释:<#--和--> [/b]
下面是一个常用的模板例子:
例一:
<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<#list animals as being>
<tr>
<td>
<#if being.size = "large"><b></#if>
${being.name}
<#if being.size="large"></b></#if>
<td>${being.price} Euros
</#list>
</table>
<#include "/copyright_footer.html">
注意点:
1.) FreeMarker是区分大小写的;
2.) FTL标记不能位于另一个FTL标记内部,例如:<#if <#include 'foo'>='bar'>...</if>;
3.) ${…}只能在文本中使用;
4.) 多余的空白字符会在模板输出时去除;
5.) 如果使用的指令不存在,会产生一个错误消息。
例子2。
<%@ taglib uri="/WEB-INF/fmtag.tld" prefix="fm" %>
<jsp:useBean id="mybean" class="freemarker.examples.jsp.SimpleBean"/>
<jsp:useBean id="mybeanreq" class="freemarker.examples.jsp.SimpleBean" scope="request"/>
<fm:template>
<html>
<head>
<title>FreeMarker JSP Example</title>
</head>
<body>
<h1>FreeMarker JSP example</h1>
<hr>
<p>
This page is a JSP page, yet most of its contents is generated using
a FreeMarker template. The below lines are the output of calling
properties on a JSP-declared bean from the FreeMarker template:
</p>
<#assign mybean = page.mybean>
<#assign mybeanreq = request.mybeanreq>
<p>page: ${mybean.string}
<#list mybean.array as item>
<br>${item}
</#list>
<br>request : ${mybeanreq.string}
<p><b>Note:</b> Starting from FreeMarker 2.2 you can use custom JSP tags in
FreeMarker templates. If you want to migrate from JSP to FTL (i.e. FreeMarker templates),
then that's probably a better option than embedding FTL into JSP pages.
</body>
</html>
</fm:template>
fmtag.tld文件:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglib_1_1.dtd">
<taglib>
<tlibversion>2.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>FreeMarker JSP Support</shortname>
<tag>
<name>template</name>
<tagclass>freemarker.ext.jsp.FreemarkerTag</tagclass>
<bodycontent>tagdependent</bodycontent>
<info>Allows evaluation of FreeMarker templates inside JSP</info>
<attribute>
<name>caching</name>
<required>false</required>
</attribute>
</tag>
</taglib>
例子3:
inedex.ftl 代码:
<#import "/lib/common.ftl" as com>
<#escape x as x?html>
<@com.page title="Index">
<a href="form.do">Add new message</a> | <a href="help.html">Help</a>
<#if guestbook?size = 0>
<p>No messages.
<#else>
<p>The messages are:
<table border=0 cellspacing=2 cellpadding=2 width="100%">
<tr align=center valign=top>
<th bgcolor="#C0C0C0">Name
<th bgcolor="#C0C0C0">Message
<#list guestbook as e>
<tr align=left valign=top>
<td bgcolor="#E0E0E0">${e.name} <#if e.email?length != 0> (<a href="mailto:${e.email}">${e.email}</a>)</#if>
<td bgcolor="#E0E0E0">${e.message}
</#list>
</table>
</#if>
</@com.page>
</#escape>
form.ftl代码
<#import "/lib/common.ftl" as com>
<#global html=JspTaglibs["/WEB-INF/struts-html.tld"]>
<#escape x as x?html>
<@com.page title="Add Entry">
<@html.errors/>
<@html.form action="/add">
<p>Your name:<br>
<@html.text property="name" size="60"/>
<p>Your e-mail (optional):<br>
<@html.text property="email" size="60"/>
<p>Message:<br>
<@html.textarea property="message" rows="3" cols="60"/>
<p><@html.submit value="Submit"/>
</@html.form>
<p><a href="index.do">Back to the index page</a>
</@com.page>
</#escape>
add.ftl代码内容
<#import "/lib/common.ftl" as com>
<#escape x as x?html>
<@com.page title="Entry added">
<p>You have added the following entry to the guestbook:
<p><b>Name:</b> ${guestbookEntry.name}
<#if guestbookEntry.email?length != 0>
<p><b>Email:</b> ${guestbookEntry.email}
</#if>
<p><b>Message:</b> ${guestbookEntry.message}
<p><a href="index.do">Back to the index page...</a>
</@com.page>
</#escape>
common.ftl的 代码
<#macro page title>
<html>
<head>
<title>FreeMarker Struts Example - ${title?html}</title>
<meta http-equiv="Content-type" content="text/html; charset=ISO-8859-1">
</head>
<body>
<h1>${title?html}</h1>
<hr>
<#nested>
<hr>
<table border="0" cellspacing=0 cellpadding=0 width="100%">
<tr valign="middle">
<td align="left">
<i>FreeMarker Struts Example</i>
<td align="right">
<a href="http://freemarker.org"><img src="poweredby_ffffff.png" border=0></a>
</table>
</body>
</html>
</#macro>
FreeMarker内部运行的某些原理:
个人感觉有兴趣和 时间的人可以去研究下《Programmer Guide》部分(原文档共有四个部分)该部分主要是介绍FreeMarker内部运行的某些原理