自由♂的marker

Apache FreeMarke是一个模板引擎:一个Java库,用于根据模板和更改数据生成文本输出(HTML网页,电子邮件,配置文件,源代码等)。模板是用FreeMarker模板语言(FTL)编写的,这是一种简单的专用语言(不像PHP这样的完整编程语言)。通常,使用通用编程语言(如Java)来准备数据(发布数据库查询,进行业务计算)

img

这篇文章呢就来介绍freemarker 的常见用法

Freemarker常用

正常显示一个变量

1
<p>${name}</p>

数据类型

定义字符串

1
2
3
4
<#assign temp = "happy fish"  />
或者
<#assign temp = 'happy fish' />
<b>${temp}</b>

这两种形式是相等的。字符串中可以使用转义字符”\”。如果字符串内有大量的特殊字符,则可以在引号的前面加上一个字母r,则字符串内的所有字符都将直接输出。例如:”It’s \”quoted\”” 或者 r”C:\raw\string”

数字类型

1
2
<#assign number = 111  />
<b>${number}</b>

输入不带引号的数字就可以直接指定一个数字,必须使用点作为小数的分隔符而不能是其他的分组分隔符。可以使用-或+来表明符号(+是多余的)。科学记数法暂不支持使用(1E3就是错误的),而且也不能在小数点之前不写0(.5也是错误的)。

哈希表类型

1
2
<#assign map = {"name":"green mouse", "price":150} />
<b>${map.name}</b>

键和值成对出现并以冒号分隔,最外面使用花括号,注意到名字和值都是表达式,但是用来检索的名字就必须是字符串类型的。

序列类型

1
2
3
4
5
6
<#assign  nums=[1,2,3,4,5,77,8,99] />
<b>${nums[0]}</b>
输出序列
<#list nums as num>
${num}
</#list>

指定一个序列,使用逗号来分隔其中的每个子变量,还可以采用数字范围定义了一个连续的序列

1
<#assign nums=1..99/>

这种方式定义的序列的内容是1到99。总之,使用数字范围也可以表示一个数字集合,如1..5等同于集合[1,2, 3, 4, 5];同样也可以用5..1来表示[5, 4, 3, 2, 1]。

时间类型

1
2
3
4
5
6
<#assign date1 = "2018-09-22"?date("yyyy-MM-dd") />
<#assign date2 ="16:34:43"?time("HH:mm:ss") />
<#assign date3 = "2018-09-22 17:23:45"?datetime("yyyy-MM-dd HH:mm:ss") />
<b>${date1},</b>
<b>${date2},</b>
<b>${date3}</b>

FreeMarker支持date、time、datetime三种类型,这三种类型的值无法直接指定,通常需要借助字符串的date、time、datetime三个内建函数进行转换才可以

布尔类型

1
2
<#assign  flag=true  />
<#if flag>fulinlin</#if>

注意boolean不能输出需要转换为字符串

1
2
<#assign  flag=true  />
<b>${flag}?string</b>

宏的基本用法

1
2
3
4
<#macro one>
<p style="font-size: x-small"> Hello Word!</p>
</#macro>
<@one></@one>

如果没有体内容也可以用 <@one />

可以在宏定义之后定义参数,宏参数是局部变量,只在宏定义中有效

1
2
3
4
<#macro two person>
<p style="font-size: x-small"> Hello ${person}!</p>
</#macro>
<@two person="fulin"></@two>

宏的参数是FTL表达式,所以,person=fulin和上面的例子中具有不同的意义,这意味着将变量fulin的值传给person,这个值可能是任意一种数据类型,甚至是一个复杂的表达式。

宏可以有多个参数,使用时参数的次序是无关的,但是只能使用宏中定义的参数,并且对所有参数赋值

1
2
3
4
<#macro three person size>
<p style="font-size: ${size}" > Hello ${person}!</p>
</#macro>
<@three size="26px" person="Word"></@three>

如果其中一个没有赋值就会出错,如果想定义个缺省值怎么办呢<#macro three person size=”22px”> 这样的话,这个使用方法就是正确的。

序列的操作

序列内置函数

迭代

1
2
3
4
5
6
7
<#assign list=[1,123,12,1234,12345,123456] />
<#list list as l>
<b>${l_index + 1}.</b>
<b>${l}</b>
<#if l=12345><#break></#if>
<#if l_has_next>,</#if>
</#list>

输出结果: 1. 1 , 2. 123 , 3. 12 , 4. 1,234 , 5. 12,345

item_index:当前变量的索引值.
item_has_next:是否存在下一个对象.

返回序列头尾值

1
2
${list?first}<br>
${list?last}<br>

序列反转

1
2
3
4
<#list list?reverse as l>
<b>${l}</b>
<#if l_has_next>,</#if>
</#list><br>

序列大小

1
${list?size}<br>

对象转化为字符串后顺序排序

1
2
3
4
<#list list?sort as l>
<b>${l}</b>
<#if l_has_next>,</#if>
</#list><br>

按序列中对象的属性value进行排序

1
2
3
4
<#list list?sort_by(value) as l>
<b>${l}</b>
<#if l_has_next>,</#if>
</#list><br>

序列的连接

1
2
3
<#list ["一","二","三"] + ["四","五","六"] as x>
${x}
</#list>

输出结果如下:一二三四五六

判断序列是否包含某个元素

1
2
3
4
<#assign x = ["cat", 16, "fish", "fulin"]>
"dog": ${x?seq_contains("dog")?string("yes", "no")}
16: ${x?seq_contains(16)?string("yes", "no")}
"16": ${x?seq_contains("16")?string("yes", "no")}

输出结果:”dog”: yes、16: yes、”16”: no,seq_前缀在这个内建函数中是需要的,用来和contains 区分开。contains函数用来在字符串中查找子串(因为变量可以同时当作字符串和序列)

Hash的内置函数

返回hash里的所有key

1
2
3
4
5
<#assign map={"name":"张三","age":"16"} />
<#list map?keys as m>
<b>${m}</b>
<#if m_has_next>,</#if>
</#list><br>

返回hash里的所有value

1
2
3
4
5
<#assign map={"name":"张三","age":"16"} />
<#list map?values as m>
<b>${m}</b>
<#if m_has_next>,</#if>
</#list><br>

字符串内置函数

从一个字符串中截取子串

1
${str?substring(1,3)}<br>

start:截取子串开始的索引,start必须大于等于0,小于等于end
end: 截取子串的长度,end必须大于等于0,小于等于字符串长度,如果省略该参数,默认为字符串长度。

首字母大写

1
${str?cap_first}<br>

首字母小写

1
${str?uncap_first}<br>

所有单词首字母大写

1
${str?capitalize}<br>

判断某个字符串是否由某个子串结尾

1
${str?ends_with("rd")?string}<br>

将字符串中的<、>、&和”替换为对应得<>&quot:&amp

1
${str?html}<br>

在字符串中查找某个子串

1
${str?index_of("d",-1)}<br>

返回找到子串的第一个字符的索引,如果没有找到子串,则返回-1。Start参数用于指定从字符串的那个索引处开始搜索,start为数字值。如果start大于字符串长度,则start取值等于字符串长度,如果start小于0,则start取值为0。

返回字符串的长度

1
${str?length}<br>

将字符串转为小写

1
${str?lower_case}<br>

将字符串转为大写

1
${str?upper_case}<br>

判断字符中是否包含某个子串

1
${str?contains("a")?string}<br>

将字符串转换为数字

1
${num?number}<br>

替换字符串

1
${str?replace("o","*")}<br>

字符串拆分

1
2
3
4
<#list str?split("o") as m>
<b>${m}</b>
<#if m_has_next>,</#if>
</#list><br>

删除字符串首尾空格

1
${str?trim}<br>

数字转换为字符串

1
${number?c?is_string?string}<br>

数字格式

1
2
3
${number?string.computer}<br>
${number?string.percent}<br>
${number?string.number}<br>

Freemarker中预订义了三种数字格式:number,currency(货币)和percent(百分比)其中number为默认的数字格式转换.

数值精度控制

1
2
3
4
5
6
<#assign x=2.582/>
<#assign y=4/>
#{x; M2}//2.58
#{y; M2}//4
#{x; m1M2}//2.58
#{y; m1M2}//4.0

mX:小数部分最小X位。MX:小数部分最大X位。

布尔值内置函数

1
foo?string("yes","no")

如果布尔值是true,那么返回”yes”,否则返回no,string用于将布尔值转换为字符串输出true转为”true”,false转换为”false”.

运算符的使用

取整运算

1
2
3
4
5
<#assign x=5>
${(x/2)?>int}//2
${1.1?int}//1
${1.999?int} //1
${-1.1?int}//-1

大于号>的使用

1
2
3
4
5
6
7
8
9
<#assign x = 4>
<#if (x>5) >
x > 5
</#if>
<!--或者-->
<#assign x = 4>
<#if x gt 5 >
x > 5
</#if>

使用>=和>的时候有一点小问题。FreeMarker解释>的时候可以把它当作FTL标签的结束符。为了避免这种问题,不得不将表达式放到括号内:<#if (x > y) >,另外,可以使用lt代替<,lte代替<=,gt代替>,gte代替>=。由于历史遗留的原因,FTL也支持\lt,\lte,\gt和\gte,使用他们和使用不带反斜杠的效果一样。切记不能直接 x>5 原因是Freemarker内部的解析处理原因。

Freemarker模板解析过程

概念啥东西,放上面基本都不看~~~~~

例如:一个freemarker表达式” ${hello} “,会被解析成三个部分,分别是

1
2
3
<body>
${hello}
</body>

前面和后面的body标签,在freemarker中被定义为TextBlock,中间的变量定义为DollarVariable。那么目前的结构也就是RootExpression = TextBlock DollarVariable TextBlock。解释器一进来将会对RootExpression进行解析,RootExpression将会依次调用TextBlock DollarVariable TextBlock进行解析。不同类型将会做不同操作,根据传进来的Context参数进行相应赋值并输出等。
当Template启动解释时,由Environment进入调用根元素的访问动作,根元素会依次访问所包含的TemplateElement,直到所有叶子节点访问完成,这些访问动作是通过调用Environment的visit方法控制,Environment做些相关必要操作,再根据访问的节点类型调用相应节点的访问操作。当访问到包含需要解释器的元素节点时,则会启动解释器做解释操作,根据Expression类型,调用getStringValue,并传入参数Environment,相应类型的表达式根据Environment解释得到输入字符串的值,返回并写到响应流,即解释完成。

FreeMarker缓存处理

FreeMarker 的缓存处理主要用于模版文件的缓存,一般来讲,模版文件改动不会很频繁,在一个流量非常大的网站中,如果频繁的读取模版文件对系统的负担还是很重的,因此 FreeMarker 通过将模版文件的内容进行缓存,来降低模版文件读取的频次,降低系统的负载。当处理某个模版时,FreeMarker直接从缓存中返回对应的 Template 对象,并有一个默认的机制来保证该模版对象是跟模版文件同步的。如果使用的时候 FreemarkerServlet 时,有一个配置项template_update_delay用来指定更新模版文件的间隔时间,相当于多长时间检测一下是否有必要重新加载模版文件,0 表示每次都重新加载,否则为多少毫秒钟检测一下模版是否更改。FreeMarker定义了一个统一的缓存处理接口CacheStorage,默认的实现是 MruCacheStorage 最近最少使用的缓存策略

Freemarker空值的处理

FreeMarker的变量必须赋值,否则就会抛出异常。而对于FreeMarker来说,null值和不存在的变量是完全一样的,因为FreeMarker无法理解null值。FreeMarker提供两个运算符来避免空值:

(1)!运算符:指定缺失变量的默认值;
(2)??运算符:判断变量是否存在。

!运算符有两种用法:variable!或variable!defaultValue。第一种用法不给变量指定默认值,表明默认值是空字符串、长度为0的集合、或长度为0的Map对象。

使用!运算符指定默认值并不要求默认值的类型和变量类型相同 比如说:

1
2
3
4
5
<#-- ${fulin}没有定义这个变量,会报异常!-->

${fulin!} <#--没有定义这个变量,默认值是空字符串!-->

${fulin!"abc"} <#--没有定义这个变量,默认值是字符串abc!-->

??运算符返回布尔值,如:variable??,如果变量存在,返回true,否则返回false。一般情况下与if指令共同使用。将它和if指令合并,如下面的例子:如果user变量不存在的话将会忽略整个问候代码段:

1
<#if user??><h1>Welcome ${user}!</h1></#if>

关于多级访问的变量,比如user.money.price,书写代码:user.money.price!0,仅当user.money存在而仅仅最后一个子变量price可能不存在(这种情况下我们假设价格是0)。如果user或者money不存在,那么模板处理过程将会以“未定义的变量”错误而停止。为了防止这种情况的发生,可以这样来书写代码(user.money.price)!0。这种情况下当animals或python不存在时表达式的结果仍然是0。对于??也是同样用来的处理这种逻辑的:user.money.price??对比(user.money.price)??来看。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×