FreeMarker是一个模板引擎,它的一个主要的原则就是:

模板 + 数据模型 = 输出

FreeMarker模板本身不是HTML文件,它有自己的语法(但是其中可以包含html元素),常用的有3种形式:

  • ${...}

    插值,即将数据模型中的值插入到模板中;

  • ftl标签(也叫做指令)

    即FreeMarker的指令,以#开头,如果是自定义的标签,则使用@替换#,如:

    <#if animals.python.price == 0>
      Pythons are free today!
    </#if>
    

    比较常见的有if/else if /else指令,list指令,include指令(用宏功能更合适)。

  • 注释

    <#-- 注释信息 --> 
    

还有一点要说的就是,FreeMarker是区分大小写的

FreeMarker中还有一些内建函数,一般使用?访问,比如:

user?html
user?upper_case
animal.name?cap_first

对于null值与不存在的变量的调用,我们可以使用一个默认值代替,要不然会发生错误,默认值需要使用!符号进行表示:

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

通常可以使用??来检查变量是否存在:

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

对于级联的访问形式,最好还是用括号进行显示划分:

(animals.python.price)!0
(animals.python.price)??

数据类型

FreeMarker支持下面这些类型:

  • 标量

    • 字符串
    • 数字
    • 布尔值
    • 日期/时间
  • 容器

    • map
    • list
    • array
  • 方法和函数

    The average of 3 and 5 is: ${avg(3, 5)}
    The average of 6 and 10 and 20 is: ${avg(6, 10, 20)}
    The average of the price of a python and an elephant is:
    ${avg(animals.python.price, animals.elephant.price)}
    
  • 用户自定义指令

    <@box title="Attention!">
      Too much copy-pasting may leads to
      maintenance headaches.
    </@box>
    

    在模板中,FreeMarker不知道的Java对象的方法通常是可以作为方法来使用的, 而不用考虑Java对象方法本身的特性,因为在这里没有其他的选择。

    通常需要返回值的可以使用方法和函数,而带有HTML结构的或者需要进行流程控制的则可以使用自定义指令。

需要注意的点

  • 插值只能用在两个地方:文本区和字符串表达式

    <h1>Hello ${name}!</h1>
    <#include "/footer/${company}.html">
    

    一种常见的错误就是~~<#if $>...</#if>~~这种调用。

  • 在文本区插值的时候需要注意转义,避免产生安全隐患

    <#escape x as x?html>
      ...
      <p>Title: ${book.title}</p>
      <p>Description: <#noescape>${book.description}</#noescape></p>
      <h2>Comments:</h2>
      <#list comments as comment>
        <div class="comment">
          ${comment}
        </div>
      </#list>
      ...
    </#escape>
    

    可以使用<#escape>指令,这样其包裹的内容特殊字符都会被转义,避免安全漏洞。

  • 布尔值的使用

    使用布尔值的时候,如果直接在插值中打印布尔值会出错,需要使用内嵌方法string转成string然后进行处理:

    ${married?string("yes", "no")}
    

自定义指令

自定义指令可以使用marco指令来定义:

<#macro greet>
  <font size="+2">Hello Joe!</font>
</#macro>

macro指令自身不输出任何内容,它只是用来创建宏变量,所以就会有一个名为greet的变量。

可以使用@来使用自定义指令:

<@greet></@greet>

当然也可以这样使用:

<@greet/>

宏中定义参数

可以在指令变量名称后面的位置,定义传递的参数:

<#macro greet person>
  <font size="+2">Hello ${person}!</font>
</#macro>

这样就可以这样使用宏指令了:

<@greet person="Fred"/> and <@greet person="Batman"/>

当然也可以定义多个参数:

<#macro greet person color>
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>

这个时候可以像下面这样使用:

<@greet person="Fred" color="black"/>

参数的顺序并不重要,但是必须给出参数定义的所有的值。

对于参数多的,写很多参数未免太过于麻烦,因此可以使用默认参数值:

<#macro greet person color="black">
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>

嵌套内容

可以使用<#nested>指令,在自定义指令间使用嵌套内容:

<#macro border>
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    <#nested>
  </tr></td></table>
</#macro>

使用如下:

<@border>The bordered text</@border>

嵌套还可以多次调用:

<#macro do_thrice>
  <#nested>
  <#nested>
  <#nested>
</#macro>
<@do_thrice>
  Anything.
</@do_thrice>

输出如下:

  Anything.
  Anything.
  Anything.

对于没用使用<#nested>指令的宏,中间的内容是不会执行的,也不会当做错误,只会忽略。

创建库

可以新建一个库:

<#macro copyright date>
  <p>Copyright (C) ${date} Julia Smith. All rights reserved.</p>
</#macro>

<#assign mail = "jsmith@acme.com">

使用的时候可以使用<#import>指令引入:

<#import "/lib/my_test.ftl" as my>
<@my.copyright date="1999-2002"/>
${my.mail}

参考文章

  1. https://freemarker.apache.org/