FreeMarker介绍及基本数据类型和用法

FreeMarker 中文官方参考手册

FreeMarker 英文官方参考手册

一、FreeMarker介绍

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

模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。

入门

模板 + 数据模型 = 输出

假设在一个在线商店的应用系统中需要一个HTML页面,和下面这个页面类似:

  1. <html>
  2. <head>
  3. <title>Welcome!</title>
  4. </head>
  5. <body>
  6. <h1>Welcome John Doe!</h1>
  7. <p>Our latest product:
  8. <a href="products/greenmouse.html">green mouse</a>!
  9. </body>
  10. </html>

这里的用户名(上面的"Big Joe"),应该是登录这个网页的访问者的名字, 并且最新产品的数据应该来自于数据库,这样它才能随时更新。那么不能直接在HTML页面中输入它们, 不能使用静态的HTML代码。此时,可以使用要求输出的 模板。 模板和静态HTML是相同的,只是它会包含一些 FreeMarker 将它们变成动态内容的指令:

  1. <html>
  2. <head>
  3. <title>Welcome!</title>
  4. </head>
  5. <body>
  6. <h1>Welcome ${user}!</h1>
  7. <p>Our latest product:
  8. <a href="${latestProduct.url}">${latestProduct.name}</a>!
  9. </body>
  10. </html>

模板文件存放在Web服务器上,就像通常存放静态HTML页面那样。当有人来访问这个页面, FreeMarker将会介入执行,然后动态转换模板,用最新的数据内容替换模板中 ${*...*} 的部分, 之后将结果发送到访问者的Web浏览器中。访问者的Web浏览器就会接收到例如第一个HTML示例那样的内容 (也就是没有FreeMarker指令的HTML代码),访问者也不会察觉到服务器端使用的FreeMarker。 (当然,存储在Web服务器端的模板文件是不会被修改的;替换也仅仅出现在Web服务器的响应中。)

请注意,模板并没有包含程序逻辑来查找当前的访问者是谁,或者去查询数据库获取最新的产品。 显示的数据是在 FreeMarker 之外准备的,通常是一些 "真正的" 编程语言(比如Java) 所编写的代码。模板作者无需知道这些值是如何计算出的。事实上,这些值的计算方式可以完全被修改, 而模板可以保持不变,而且页面的样式也可以完全被修改而无需改动模板。 当模板作者(设计师)和程序员不是同一人时,显示逻辑和业务逻辑相分离的做法是非常有用的, 即便模板作者和程序员是一个人,这么来做也会帮助管理应用程序的复杂性。 保证模板专注于显示问题(视觉设计,布局和格式化)是高效使用模板引擎的关键。

为模板准备的数据整体被称作为 数据模型。 模板作者要关心的是,数据模型是树形结构(就像硬盘上的文件夹和文件),在视觉效果上, 数据模型可以是:

  1. (root)
  2. |
  3. +- user = "Big Joe"
  4. |
  5. +- latestProduct
  6. |
  7. +- url = "products/greenmouse.html"
  8. |
  9. +- name = "green mouse"

Note:

上面只是一个形象化显示;数据模型不是文本格式,它来自于Java对象。 对于Java程序员来说,root就像一个有 getUser()getLatestProduct() 方法的Java对象, 也可以有 "user""latestProducts" 键值的Java Map对象。相似地,latestProduct 就像是有 getUrl()getName() 方法的Java对象。

早期版本中,可以从数据模型中选取这些值,使用 userlatestProduct.name 表达式即可。如果我们继续类推, 数据模型就像一个文件系统,那么 "(root)" 和 latestProduct 就对应着目录(文件夹),而 user, urlname 就是这些目录中的文件。

总的来说,模板和数据模型是FreeMarker来生成输出(比如第一个展示的HTML)所必须的:

模板 + 数据模型 = 输出

模板一览

最简单的模板通常是普通的HTML文件(或者是其他任何文本文件; FreeMarker本身不属于HTML)。当客户端访问某个页面时, FreeMarker要发送HTML代码至客户端浏览器中去显示。如果想要页面动起来 (这里指动态网页技术,译者注),那么就要在HTML中放置能被FreeMarker所解析的特殊代码片段:

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

其他任何不是FTL标签,插值或注释的内容将被视为静态文本, 这些东西不会被FreeMarker所解析;会被按照原样输出出来。

FTL标签也被称为 指令。 这些指令在HTML的标签 (比如: <table></table>) 和HTML元素 (比如: table 元素) 中的关系是相同的。(如果现在还没有感觉到它们的不同, 那么把“FTL标签”和“指令”看做是同义词即可。)

Note:

可以在 http://freemarker-online.kenshoo.com/ 上很方便的尝试编写模板

基本指令

这里我们仅仅来看一些非常常用的指令,当然 (指令还有很多)。

if 指令

使用 if 指令可以有条件地跳过模板的一些片段。 比如,假设在 最初的示例 中, 想向你的老板Big Joe特别地问好,可其他人不同:

  1. <html>
  2. <head>
  3. <title>Welcome!</title>
  4. </head>
  5. <body>
  6. <h1>
  7. Welcome ${user}<#if user == "Big Joe">, our beloved leader</#if>!
  8. </h1>
  9. <p>Our latest product:
  10. <a href="${latestProduct.url}">${latestProduct.name}</a>!
  11. </body>
  12. </html>

此时,告诉 FreeMarker,当和 "Big Joe" 相同时 ", our beloved leader" (我们最尊敬的领导,译者注) 才是if条件中那唯一的 user 变量的值。 通常来讲,如果 *condition* 是false(布尔值),那么介于 <#if *condition*></#if> 标签中的内容会被略过。

我们来详细说说 *condition* 的使用: == 是用来判断它两侧的值是否相等的操作符, 比较的结果是布尔值,也就是true或者false。在 == 的左侧,是 被引用的变量, 我们很熟悉这样的语法结构;最终它会被变量的值所替代。通常来说, 在指令或插值中没有被引号标注的内容都被视为变量的引用。右侧则是指定的字符串, 在模板中的字符串 只能 放在引号内。

当价格为0时,就会打印出 "Pythons are free today!":

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

和之前示例中,字符串被直接指定相似, 但这里则是数字(0)被直接指定了。 请注意,这里的数字 没有 放在引号内。 如果将("0")放在引号中, 那么FreeMarker就会将其误判为字符串了(也就是字符串0,译者注)。

当价格不为0时,则会打印出"Pythons are not free today!":

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

你也许就会猜测了, != 就是"不等于"。

同时,也可以这样编来写代码(使用 数据模型来描述哈希表):

  1. <#if animals.python.price < animals.elephant.price>
  2. Pythons are cheaper than elephants today.
  3. </#if>

使用 <#else> 标签可以指定当条件为false时程序所要执行的内容。比如:

  1. <#if animals.python.price < animals.elephant.price>
  2. Pythons are cheaper than elephants today.
  3. <#else>
  4. Pythons are not cheaper than elephants today.
  5. </#if>

这个示例中,如果蟒蛇的价格比大象的价格低的话, 程序将会打印出 "Pythons are cheaper than elephants today."。 否则会打印 "Pythons are not cheaper than elephants today."。 后面也可以使用 elseif 来完善它:

  1. <#if animals.python.price < animals.elephant.price>
  2. Pythons are cheaper than elephants today.
  3. <#elseif animals.elephant.price < animals.python.price>
  4. Elephants are cheaper than pythons today.
  5. <#else>
  6. Elephants and pythons cost the same today.
  7. </#if>

如果变量本身就是布尔值(true/false),则可以直接让其作为 if*condition* (判断条件,译者注):

  1. <#if animals.python.protected>
  2. Pythons are protected animals!
  3. </#if>

list 指令

当需要列表显示内容时,list指令是必须的。比如: 如果合并该模板到 前面描述序列的数据模型 中:

  1. <p>We have these animals:
  2. <table border=1>
  3. <#list animals as animal>
  4. <tr><td>${animal.name}<td>${animal.price} Euros
  5. </#list>
  6. </table>

那么输出结果将会是这样的:

  1. <p>We have these animals:
  2. <table border=1>
  3. <tr><td>mouse<td>50 Euros
  4. <tr><td>elephant<td>5000 Euros
  5. <tr><td>python<td>4999 Euros
  6. </table>

list 指令的一般格式为: <#list *sequence* as *loopVariable*>*repeatThis*</#list>*repeatThis* 部分将会在给定的 *sequence* 遍历时在每一项中重复, 从第一项开始,一个接着一个。在所有的重复中, *loopVariable* 将持有当前遍历项的值。 这个变量仅存在于 <#list *...*></#list> 标签内。

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

  1. <ul>
  2. <#list misc.fruits as fruit>
  3. <li>${fruit}
  4. </#list>
  5. </ul>

你应该很熟悉表达式 misc.fruits 了; 它 引用了数据模型中的变量

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

  1. <#list misc.fruits>
  2. <ul>
  3. <#items as fruit>
  4. <li>${fruit}
  5. </#items>
  6. </ul>
  7. </#list>

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

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

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

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

再次回到这个话题,如果我们有0个水果,会怎么样?只是打印 "Fruits:" 也没有什么不方便。 list 指令,也像 if 指令那样,可以有 else 部分,如果列表中有0个元素时就会被执行:

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

Note:

事实上,这个过于简单的示例可以这么来写, 但是它使用了本主题中没有介绍的语言特性:

  1. <p>Fruits: ${fruits?join(", ", "None")}

所有的这些指令(list, items, sep, else)可以联合起来使用:

  1. <#list misc.fruits>
  2. <p>Fruits:
  3. <ul>
  4. <#items as fruit>
  5. <li>${fruit}<#sep> and</#sep>
  6. </#items>
  7. </ul>
  8. <#else>
  9. <p>We have no fruits.
  10. </#list>

Note:

指令参考 中, 可以获取到更多关于这些指令的内容。

include 指令

使用 include 指令, 我们可以在模板中插入其他文件的内容。

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

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

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

  1. <html>
  2. <head>
  3. <title>Test page</title>
  4. </head>
  5. <body>
  6. <h1>Test page</h1>
  7. <p>Blah blah...
  8. <#include "/copyright_footer.html">
  9. </body>
  10. </html>

此时,输出的内容为:

  1. <html>
  2. <head>
  3. <title>Test page</title>
  4. </head>
  5. <body>
  6. <h1>Test page</h1>
  7. <p>Blah blah...
  8. <hr>
  9. <i>
  10. Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
  11. <br>
  12. All Rights Reserved.
  13. </i>
  14. </body>
  15. </html>

当修改了 copyright_footer.html 文件, 那么访问者在所有页面都会看到版权声明的新内容。

Note:

重用代码片段的一个更有力的方式是使用宏,但是只是更为高级的话题了, 将会在 后续讨论

联合使用指令

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

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

请注意,FreeMarker并不解析FTL标签以外的文本、插值和注释, 上面示例在HTML属性中使用FTL标签也不会有问题。

使用内建函数

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

  • user?html 给出 user 的HTML转义版本, 比如 & 会由 & 来代替。
  • user?upper_case 给出 user 值的大写版本 (比如 "JOHN DOE" 来替代 "John Doe")
  • animal.name?cap_first 给出 animal.name 的首字母大写版本(比如 "Mouse" 来替代 "mouse")
  • user?length 给出 user 值中 字符的数量(对于 "John Doe" 来说就是8)
  • animals?size 给出 animals 序列中 项目 的个数(我们示例数据模型中是3个)
  • 如果在 <#list animals as animal> 和对应的 </#list> 标签中:
    • animal?index 给出了在 animals 中基于0开始的 animal的索引值
    • animal?counter 也像 index, 但是给出的是基于1的索引值
    • animal?item_parity 基于当前计数的奇偶性,给出字符串 "odd" 或 "even"。在给不同行着色时非常有用,比如在 <td class="${animal?item_parity}Row">中。

一些内建函数需要参数来指定行为,比如:

  • animal.protected?string("Y", "N") 基于 animal.protected 的布尔值来返回字符串 "Y" 或 "N"。
  • animal?item_cycle('lightRow','darkRow') 是之前介绍的 item_parity 更为常用的变体形式。
  • fruits?join(", ") 通过连接所有项,将列表转换为字符串, 在每个项之间插入参数分隔符(比如 "orange,banana")
  • user?starts_with("J") 根据 user 的首字母是否是 "J" 返回布尔值true或false。

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

可以阅读 全部内建函数参考

处理不存在的变量

数据模型中经常会有可选的变量(也就是说有时并不存在)。 除了一些典型的人为原因导致失误外,FreeMarker 绝不能容忍引用不存在的变量, 除非明确地告诉它当变量不存在时如何处理。这里来介绍两种典型的处理方法。

这部分对程序员而言: 一个不存在的变量和一个是 null 值的变量, 对于FreeMarker来说是一样的,所以这里所指的"丢失"包含这两种情况。

不论在哪里引用变量,都可以指定一个默认值来避免变量丢失这种情况, 通过在变量名后面跟着一个 !(叹号,译者注)和默认值。 就像下面的这个例子,当 user 不存在于数据模型时, 模板将会将 user 的值表示为字符串 "visitor"。(当 user 存在时, 模板就会表现出 ${user} 的值):

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

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

  1. <#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)??来看。

二、FreeMarker常用语法

转载至:https://www.cnblogs.com/anyun/p/9426479.html

一、基本数据类型

  1. 布尔型:等价于java中的boolean类型, 不同的是不能直接输出,可以转换成字符串再输出

  2. 日期型:等价于java中的Date类型, 不同之处在于不能直接输出,需要转换成字符串再输出

  3. 数值型:等价于java 中的int, float, double 等数值类型,有三种显示形式:数值型(默认) 、货币型、百分比型

  4. 字符串型:等价于java 中的字符串,有很多内置函数

  5. sequence 类型:等价于java中的数组,list,set 等集合类型

  6. hash 类型:等价于java 中的Map 类型

二、 数据类型示例

【1. 布尔型】
  1. 不能直接输出布尔型的值, 必须转换为string:$

  2. 在if标签中可以直接使用

    1. <#if b>
    2. b 的值为 true
    3. </#if>
【2. 日期型】
  1. 输出日期:$
  2. 只输出时间:$
  3. 输出日期时间:$
  4. 格式化日期: $
【3. 数值型】
1. Freemarker 中预定义了三种数字格式,货币,百分比,数字,默认为数字格式

​ 货币::${0.3?string.currency}

​ 百分比:${0.3?string.percent}

​ 数字(默认):${0.3?string.number}

2.取整
  1. 向上取整

    1. 3.4 --> ${3.4?ceiling}
    2. 3.5 --> ${3.5?ceiling}
  2. 向下取整

    1. 3.4 --> ${3.4?floor}
    2. 3.5 --> ${3.5?floor}
  3. 四舍五入

    1. 3.4 --> ${3.4?round}
    2. 3.5 --> ${3.5?round}
    3. 数字格式化, 使用0 表示不够 由0 补齐, 用# 表示不够不补齐
  4. 保留两位小数: 必须两位,不够补0, 当前一位为偶数时,五舍六入, 当前一位为基数时,四舍五入

    1. 0.135 -- > ${0.135?string('.00')}
    2. 0.125 -- > ${0.125?string('.00')}
    3. 0.1 -- > ${0.1?string('.00')}
    1. 保留两位小数: 最多两位,不够不补0, 当前一位为偶数时,五舍六入, 当前一位为基数时,四舍五入

      1. 0.135 -- > ${0.135?string('#.##')}
      2. 0.125 -- > ${0.125?string('#.##')}
      3. 0.1 -- > ${0.1?string('#.##')}
  5. 格式化整数, 用0 表示必须三位整数,不够由0 补齐

    1. 12.1 -- > ${12.1?string('000.00')}
    2. 12.125 -- > ${12.125?string('000.00')}
    3. 12.135 -- > ${12.135?string('000.00')}
  6. 格式化整数, 用0 表示必须三位整数,不够由0 补齐, 一个# 和 多个# 是一样的

    1. 12.1 -- > ${12.1?string('#.00')}
    2. 12.125 -- > ${12.125?string('#.00')}
    3. 12.135 -- > ${12.135?string('#.00')}
  7. 千位分割

    1. 123456789 --> ${123456789?string(',###')}
    2. 23456789 --> ${123456789?string(',####')}
4 数字转换成字符串:

​ 数字转换成字符串后,就可以直接用字符串的内置函数了

  1. 1234 -- > ${123?string}
  2. 1234 -- > ${123?string[0]}
  3. <#-- ${123[2]} 报错 -->
**【4. 字符串型】

4.1 截取字符串subString(start,end):"hello,wold"**

  1. 截取

    1. 6~end: ${"hello,wold"?substring(6)}
  2. 截取

    1. 0~5: ${"Hello,World"?substring(0,5)}
4.2 字母大小写转换
  1. 首个单词的首字母大写: ${"hello world"?cap_first}

    1. 首个单词的首个字母母小写: ${"Hello World"?uncap_first}
  2. 所有单词首字母大写:${"hello world"?capitalize}

  3. 字符串大写: ${"hello,world"?upper_case}

  4. 字符串小写:${"hello,world"?lower_case}

4.3 判断是否以xxx 结尾

  1. ${"hello,world"?ends_with("world")?string} `
  2. <#if "hello,world"?ends_with("world")>
  3. hello,world 以字符串 world 结尾
  4. </#if>

4.4 判断是否以xxx 开头

  1. ${"hello,world"?starts_with("hello")?string}
  2. <#if "hello,world"?starts_with("hello")>
  3. hello,world 以字符串 hello 开头
  4. </#if>

4.5 返回字符串长度

${"hello,world"?length}

4.6 是否包含子串

  1. 返回为布尔值,布尔值不能直接输出,必须转换为string
  2. ${"hello,world"?contains("llo")?string};
  3. <#if "hello,world"?contains("llo")>
  4. "hello,world" 包含子串 "llo"
  5. </#if>

4.7 去除首尾空格

字符串:${" hello,world "?trim}

4.8 替换字符串

${"hello,world"?replace("o","0")}

4.9 查询字符串第一次出现的索引位置,如果不存在返回0

  1. ${"hello,world"?index_of("o")}
  2. ${"hello,world"?index_of("aaa")}

4.10 字符串分割数组

  1. <#assign citys="beijing,tianjin,shanghai"?split(",")/>
  2. <#list citys as city>
  3. ${city_index} --> ${city}
  4. </#list>

4.11 输出单个字母

${"hello"[0]}

【5. sequence】

1. 获取第一个元素:sequence?first

  1. array: ${cityArray?first}
  2. list: ${cityList?first}
  3. set: ${citySet?first}

2. 获取最后一个元素:sequence?last

  1. array: ${cityArray?last}
  2. list: ${cityList?last}
  3. set: ${citySet?last}

3. 返回sequence 的大小sequence?size

  1. array: ${cityArray?size}
  2. list: ${cityList?size}
  3. set: ${citySet?size}

4. 排序:sequence?sort

  1. sequence 元素为基本元素时(能转换为String的元素,非sequence 和 hash 元素)
  1. array:sort,reverse
  2. 序:<#list cityArray as city>${city},</#list>
  3. 序:<#list cityArray?reverse as city>${city},</#list>
  4. 序:<#list cityArray?sort as city>${city},</#list>
  5. 序:<#list cityArray?sort?reverse as city>${city},</#list>
  6. list:sort,reverse
  7. 序:<#list cityList as city>${city},</#list>
  8. 序:<#list cityList?reverse as city>${city},</#list>
  9. 序:<#list cityList?sort as city>${city},</#list>
  10. 序:<#list cityList?sort?reverse as city>${city},</#list>
  11. set:sort,reverse
  12. 序:<#list citySet as city>${city},</#list>
  13. 序:<#list citySet?reverse as city>${city},</#list>
  14. 序:<#list citySet?sort as city>${city},</#list>
  15. 序:<#list citySet?sort?reverse as city>${city},</#list>
  1. sequence 元素为JavaBean时

    1. 序:
    2. <#list department.employees as employee>
    3. ${employee_index} --> ${employee.name} --> ${employee.age} --> ${employee.sex}
    4. </#list>
    5. 序:
    6. <#list department.employees?reverse as employee>
    7. ${employee_index} --> ${employee.name} --> ${employee.age} --> ${employee.sex}
    8. </#list>
    9. name属性升序:
    10. <#list department.employees?sort_by("name") as employee>
    11. ${employee_index} --> ${employee.name} --> ${employee.age} --> ${employee.sex}
    12. </#list>
    13. name属性降序:
    14. <#list department.employees?sort_by("name")?reverse as employee>
    15. ${employee_index} --> ${employee.name} --> ${employee.age} --> ${employee.sex}
    16. </#list>

    5. 遍历sequence, 包含索引值

    1. array: <#list cityArray as city>
    2. ${city_index} --> ${city}
    3. </#list>
    4. list: <#list cityList as city>
    5. ${city_index} --> ${city}
    6. </#list>
    7. set: <#list citySet as city>
    8. ${city_index} --> ${city}
    9. </#list>

6. 根据索引获取sequence 元素

  1. array: ${cityArray[0]}
  2. list: ${cityList[0]}
  3. set: ${citySet[0]}

【6. map 类型】

1. map长度:${cityMap?size};

2. map的keys:cityMap.keys 返回的是一个sequence,类似于数组,所以不能直接输出,需要遍历

  1. <#assign mapKeys=cityMap?keys/>
  2. <#list mapKeys as mapKey>
  3. ${mapKey}
  4. </#list>

3. map的values: cityMap.values 返回的是一个sequence,类似于数组,所以不能直接输出,需要遍历

  1. <#assign mapValues=cityMap?values/>
  2. <#list mapValues as mapValue>
  3. ${mapValue}
  4. </#list>

4. 遍历map 元素: map 通过key获取value的方法用[]

  1. <#list cityMap?keys as key>
  2. ${key_index} --> ${key} --> ${cityMap[key]}
  3. </#list>

**【7. JavaBean 类型】

1. 获取属性:${department.id} --> ${department.name}

2. 级联属性${department.employees[0].name} --> ${department.employees[0].age} --> ${department.employees[0].sex}

3. 遍历数组:

  1. <#list department.employees as employee>
  2. ${employee_index} --> ${employee.name} --> ${employee.age} --> ${employee.sex}
  3. </#list>

4. 排序

  1. <#list department.employees?sort_by("name") as employee>
  2. ${employee_index} --> ${employee.name} --> ${employee.age} --> ${employee.sex}
  3. </#list>

三、maven依赖

https://mvnrepository.com/artifact/org.freemarker/freemarker

  1. <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
  2. <dependency>
  3. <groupId>org.freemarker</groupId>
  4. <artifactId>freemarker</artifactId>
  5. <version>2.3.31</version>
  6. </dependency>

FreeMarker介绍及基本数据类型和用法的更多相关文章

  1. Python 数据类型及其用法

    本文总结一下Python中用到的各种数据类型,以及如何使用可以使得我们的代码变得简洁. 基本结构 我们首先要看的是几乎任何语言都具有的数据类型,包括字符串.整型.浮点型以及布尔类型.这些基本数据类型组 ...

  2. day01-day04总结- Python 数据类型及其用法

    Python 数据类型及其用法: 本文总结一下Python中用到的各种数据类型,以及如何使用可以使得我们的代码变得简洁. 基本结构 我们首先要看的是几乎任何语言都具有的数据类型,包括字符串.整型.浮点 ...

  3. 7.1 安装软件包的三种方法 7.2 rpm包介绍 7.3 rpm工具用法 7.4 yum工具用法 7.5 yum搭建本地仓库

    7.1 安装软件包的三种方法 7.2 rpm包介绍 7.3 rpm工具用法 7.4 yum工具用法 7.5 yum搭建本地仓库 三种方法 rpm工具----->类型windows下的exe程序 ...

  4. Mybatis中动态SQL语句中的parameterType不同数据类型的用法

    Mybatis中动态SQL语句中的parameterType不同数据类型的用法1. 简单数据类型,    此时#{id,jdbcType=INTEGER}中id可以取任意名字如#{a,jdbcType ...

  5. openresty开发系列12--lua介绍及常用数据类型简介

    openresty开发系列12--lua介绍及常用数据类型简介 lua介绍  1993 年在巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de ...

  6. Redis详解(三)------ redis的五大数据类型详细用法

    我们说 Redis 相对于 Memcache 等其他的缓存产品,有一个比较明显的优势就是 Redis 不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据 ...

  7. Redis 详解 (三) redis的五大数据类型详细用法

    目录 1.string 数据类型 2.hash 数据类型 3.list 数据类型 4.set 数据类型 5.zset 数据类型 6.系统相关命令 7.key 相关命令 我们说 Redis 相对于 Me ...

  8. day 50 js-part1基础语法,数据类型及用法,流程控制语句,循环

    js基本概念: JavaScript 是世界上最流行的脚本语言. JavaScript 被数百万计的网页用来改进设计.验证表单.检测浏览器.创建cookies,以及更多的应用. JavaScript ...

  9. Python数据类型的用法

    字符串的用法 res = 'hellow,world' print(res) #res.显示的都是它的方法,下划线的除外 1 判断字符串的结尾字符,返回的值的布尔形式 endswith 判断字符串的开 ...

  10. Redis的介绍和常用数据类型结构命令的总结

    我们先来看一下redis的一个定义,来自官方的: Redis is an open source, BSD licensed, advanced key-value store. It is ofte ...

随机推荐

  1. NVME(学习笔记二)—CMB

    什么是CMB 在NVMe Express 1.2 Spec中开始支持一个特性,那就是CMB(Controller Memory Buffer),是指SSD控制器内部的读写存储缓冲区,与HMB(Host ...

  2. java 从零开始手写 redis(十)缓存淘汰算法 LFU 最少使用频次

    前言 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? ...

  3. 超简单JSP人员信息管理系统(适合新手练手用)

    自己以前闲着没事写的JSP小项目,适合刚学完JSP拿来练手的朋友. 源码地址: https://github.com/mudfish/userManager 项目说明: 软件需求: 开发工具:ecli ...

  4. Innodb 存储引擎表

    目录 索引组织表 Innodb逻辑存储结构 表空间 段 区 页 行 Innodb 行记录格式 Compact Redundant 行溢出数据 Compressed 和 Dynamic 行记录格式 ch ...

  5. golang中的接口(数据类型)

    golang中的接口 Golang 中的接口是一种抽象数据类型,Golang 中接口定义了对象的行为规范,只定义规范 不实现.接口中定义的规范由具体的对象来实现,通俗的讲接口就一个标准,它是对一个对象 ...

  6. MySQL写入SQL整个执行流程

    innodb存储引擎中一条sql写入的详细流程     第0步:会先去看缓冲区有没有这条数据,如果有就不进行缓存,直接进入第三步.   第1步:会将要修改的那一行数据所在的一整页加载到缓冲池Buffe ...

  7. mysql进阶语句优化---day40

    # ###part1: sql语句优化 #(1) mysql 执行流程 客户端: 发送连接请求,然后发送增删改查sql语句进行执行 服务端: 1.连接层:提供和客户端连接的服务,在tcp协议下 提供多 ...

  8. error LNK2019: unresolved external symbol _CrtDbgReport referenced in function xxxx

    现象 在用C++调试dll动态库的时候汇报上述错误,我用VS2015编译的DEBUG版本动态库,实际在VS2019中调试 解决方法 在工程属性中做出如下选择,代码生成->多线程调试DLL ,出现 ...

  9. DataGear 数据可视化看板整合前端框架Vue

    DataGear 看板JS对象的loadUnsolvedCharts()函数,用于异步加载页面端动态生成的图表元素,利用它,可以很方便整合Angular.React.Vue等前端框架. 本文以Vue为 ...

  10. 终端SSH远程连接CentOS报错:-bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory

    终端SSH远程连接CentOS时,报以下错误提示: -bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such ...