侧边栏壁纸
博主头像
极客手札博主等级

Do everything!

  • 累计撰写 33 篇文章
  • 累计创建 16 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

FreeMarker学习

什么是FreeMarker?

FreeMarker是一款模版引擎:即一种基于模版和要改变的数据,并用来生成文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

入门

模版 + 数据 = 输出

模版

<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${user}!</h1>
  <p>Our latest product:
  <a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>

数据

(root)
  |
  +- user = "Big Joe"
  |
  +- latestProduct
      |
      +- url = "products/greenmouse.html"
      |
      +- name = "green mouse"

通过编写模版,然后在需要输出时提供相应数据,替换模版中${}的部分,就可以生成对应内容的输出。
为模版准备的数据整体被称作为数据模型。模版作者要关心的是,数据模型是树形结构(就像硬盘上的文件夹和文件)。
总的来说,模版和数据模型是FreeMarker来生成输出所必须的,即 模版 + 数据模型 = 输出。

数据模型

数据模型的基本结构是树状的,这棵树可以很复杂,并且可以有很大的深度,比如:

(root)
  |
  +- animals
  |   |
  |   +- mouse
  |   |   |   
  |   |   +- size = "small"
  |   |   |   
  |   |   +- price = 50
  |   |
  |   +- elephant
  |   |   |   
  |   |   +- size = "large"
  |   |   |   
  |   |   +- price = 5000
  |   |
  |   +- python
  |       |   
  |       +- size = "medium"
  |       |   
  |       +- price = 4999
  |
  +- message = "It is a test"
  |
  +- misc
      |
      +- foo = "Something"

上图中的变量扮演目录的角色(比如rootanimalsmouseelephantpythonmisc)被称为hashes(哈希表或者哈希)。哈希表存储其他变量(被称为自变量),它们可以通过名称来查找(比如"animals","mouse","price")。
存储单值的变量(sizepricemessagefoo)称为scalars(标量)。
如果要在模板中使用自变量,那应该从根root开始指定它的路径,每级之间用点来分隔开,要访问 mouseprice,要从root开始,首先进入到animals,之后访问 mouse,最后访问 price。就可以这样来写 animals.mouse.price
另外一种很重要的变量是sequences(序列)。他们像哈希表那样存储自变量,但是子变量没有名字,他们只是列表中的项。比如,在下面这个数据模型中,animalsmisc.fruits 就是序列:

(root)
  |
  +- animals
  |   |
  |   +- (1st)
  |   |   |
  |   |   +- name = "mouse"
  |   |   |
  |   |   +- size = "small"
  |   |   |
  |   |   +- price = 50
  |   |
  |   +- (2nd)
  |   |   |
  |   |   +- name = "elephant"
  |   |   |
  |   |   +- size = "large"
  |   |   |
  |   |   +- price = 5000
  |   |
  |   +- (3rd)
  |       |
  |       +- name = "python"
  |       |
  |       +- size = "medium"
  |       |
  |       +- price = 4999
  |
  +- misc
      |
      +- fruits
          |
          +- (1st) = "orange"
          |
          +- (2nd) = "banana"

要访问序列的子变量,可以使用方括号形式的数字索引下标。索引下标从0开始,那么第一项的索引就是0,第二项的索引就是1等等。
例子:

要得到第一个动物的名称的话,可以使用 animals[0].name。
要得到 misc.fruits中的第二项(字符串"banana")可以这么来写 misc.fruits[1]。

标量类型可以分为如下的类别:

  • 字符串:就是文本,也就是任意的字符序列,也就是任意的字符序列,比如上面提到的 "m","o","u","s","e"。比如name和size也是字符串。
  • 数字:这是数值类型,就像上面的price。在FreeMarker中,字符串"50"和数字50市两种完全不同的东西。前者是两个字符的序列,而后者则是可以在数学运算中直接被使用的值。
  • 日期/时间:可以是日期-时间格式(存储某一天的日期和时间),或者市日期(只有日期,没有时间),或者市时间(只有时间,没有日志)。
  • 布尔值:对应这对/错(是/否、开/关等值)类似的值。

模板一览

  • ${...}FreeMarker将会输出真实的值来替换大括号内的表达式,这样的表达式被称为interpolation
  • FTL标签FreeMarker模板的语言标签):FTL标签和HTML标签有一些相似之处,但是它们市FreeMarker的指令,是不会在输出中打印的。这些标签的名字以#开头。(用户自定义的FTL标签则需要使用@来代替#
  • 注释:注释和HTML的注释也很相似,但是它们使用 <#-- and -->来标识。不像HTML注释那样,FTL注释不会出现在输出中,因为FreeMarker会跳过它们。

基本指令

if 指令

格式

<#if 布尔值>
  Pythons are free today!
</#if>

其中布尔值使用数值等判断语句实现。当布尔值为真,则打印Pythons are free today!

list 指令

格式

<#list sequence as loopVariable>repeatThis</#list>

repeateThis不分将会在给定的sequence 遍历时在每一项中重复,从第一项开始,一个接着一个。在所有的重复中, loopVariable 将持有当前遍历项的值。这个变量仅存在于<#list ...></#list>标签内。

sequence 可以市任意表达式,比如我们可以列表显示示例数据模型中的水果,就像这样:

<ul>
<#list misc.fruits as fruit>
  <li>${fruit}
</#list>
</ul>

上面示例中的一个问题是如果我们有0个水果,它仍然会输出一个空的 <ul></ul>,而不是什么都没有。要避免这样的情况,可以这么来使用list

<#list misc.fruits>
  <ul>
    <#items as fruit>
      <li>${fruit}
    </#items>
  </ul>
</#list>

此时,list 指令将列表视为一个整体,在items指令中的部分才会为每个水果重复。如果我们有0个水果,那么在 list 中的所有东西都被忽略过了,因此就不会有 ul 标签了。

另一个列表相关的常见任务是:使用一些分隔符来列出水果,比如逗号:

<p>Fruits: <#list misc.fruits as fruit>${fruit}<#sep>, </#list>
<p>Fruits: orange, banana

sep 覆盖的部分(也可以写成:...<#sep>,</#sep></#list>)只有当还有下一项时才会被执行。因此最后一个水果后面不会有逗号。

list 指令,也像 if 指令那样,可以有 else 部分,如果列表中有0个元素时就会被执行:

<p>Fruits: <#list misc.fruits as fruit>${fruit}<#sep>, <#else>None</#list>

所有这些指令(listitemssepelse)可以联合起来使用:

<#list misc.fruits>
  <p>Fruits:
  <ul>
    <#items as fruit>
      <li>${fruit}<#sep> and</#sep>
    </#items>
  </ul>
<#else>
  <p>We have no fruits.
</#list>

include 指令

使用 include 指令,我们可以在模板中插入其他文件的内容。
假设要在一些页面中显示版权声明的信息。那么可以创建一个文件夹来单独包含这些版权声明,之后在需要它的地方插入即可。比方说,我们可以将版权信息单独存放在页面文件 copyright_footer.html 中:

<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>

当需要用到这个文件时,可以使用 include 指令来插入:

<html>
<head>
  <title>Test page</title>
</head>
<body>
  <h1>Test page</h1>
  <p>Blah blah...
  <#include "/copyright_footer.html">
</body>
</html>

联合使用指令

在页面上也可以多次使用指令,而且指令间也可以很容易地相互嵌套。比如,在 list 指令中嵌套 if 指令:

<#list animals as animal>
      <div<#if animal.protected> class="protected"</#if>>
        ${animal.name} for ${animal.price} Euros
      </div>
</#list>

注意:FreeMarker并不解析FTL标签以外的文本、插值和注释。

使用内建函数

内建函数很像子变量(或者说java中的方法),它们并不是数据模型中的东西,是 FreeMarker 在数值上添加的。为了清晰子变量是哪部分,使用?(问号)代替.(点)来访问它们。
常见内建函数示例:

  • user?html给出userHTML转义版本,比如&会由&amp;来代替。
  • user?upper_case给出user值的大写版本(比如"JOHN DOE"来代替"John Doe")。
  • animal.name?cap_first给出animal.name的首字母大写版本(比如"Mouse"替代"mouse")。
  • user?length给出user的值中字符的数量(对于"John Doe"来说就是8)。
  • animals?size给出animal序列中项目的个数(我们示例数据模型中是3个)。
  • 如果在<#list animals as animal>和对应的</#list>标签中:
    • animal?index给出了animals中基于0开始的animal的索引值。
    • animal?counter也像index,但是给出的是基于1的索引值。
    • animal?item_party基于当前计数的奇偶性,给出字符串"odd"或"even"。在给不同行着色时非常有用,比如在<td class="${animal?item_parity}Row">中。
      一些内建函数需要参数来指定行为,比如:
  • animal.protected?string("Y","N")基于animal.protected的布尔值来返回字符串"Y"或"N"。
  • animal?item_cycle('lightROw','darkRow')是之前介绍的item_party更为常用的变体形式。
  • fruits?join(",")通过连接所有项,将列表转为字符串,在每个项之间插入参数分隔符(比如"orange,banana")
  • user?starts_with("J")根据user的首字母是否是"J"返回布尔值true或者false。

内建函数应用可以链式操作,比如user?upper_case?html会先转换用户名到大写形式,之后再进行HTML转义。(类似链式调用 .(点)一样)

处理不存在的变量

数据模型中经常会有可选的变量(也就是说有时并不存在)。除了一些典型的人为原因导致失误外,FreeMarker决不能容忍引用不存在的变量,除非明确地告诉它当变量不存在时如何处理。
不论在哪里引用变量,都可以指定一个默认值来避免丢失这种情况,通过在变量名后面跟着一个!和默认值。如下所示,当user不存在于数据模型时,模版将会将user的值表示为字符创"visitor"。(当user存在时,模版就会表现出 ${user}的值):

<h1>Welcome ${user!"visitor"}!</h1>

也可以在变量名后面通过放置??来询问一个变量是否存在。将它和if指令合并,那么如果user变量不存在的话将会忽略整个问候的代码段:

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

关于多级访问的变量,比如animals.python.price,代码animals.python.price!0当且仅当animals。python永远存在,而仅仅最后一个子变量price可能不存在时是正确的(这种情况下我们假设价格是0)。如果animalspython不存在,那么模版处理过程将会以"未定义的变量"错误而终止。为了放置这种情况的发生,可以使用如下代码(animals.python.price)!0。这种情况就是说animalspython不存在时,表达式的结果是0。对于??也是同样用来处理这种逻辑的,将animals.python.price??对比(animals.python.price)??来看。

0

评论区