java 模板引擎FreeMaker语法详解

  • 简介
  • 第一步
  • 基本指令
  • 1. if: 同java语法,用于判断
  • 2. else: 当if条件为false时执行
  • 3. elseif : 当有多个判断条件时可使用elseif
  • 4. list: 当数据类型为列表时,则使用list遍历
  • 5. #items
  • 6.sep 只有当还有下一项时才会被执行
  • 7. include 在模板中插入其他文件
  • 常用内建函数
  • 如何处理不存在变量
  • demo示例


简介

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

如下图这样的测试报告,因为整体框架不变,只是数据的变化,这样的场景就非常适合用模板。

java有模板元编程嘛 java模板语法_freemarker


java有模板元编程嘛 java模板语法_java有模板元编程嘛_02

第一步

pom.xml中添加jar包

<dependency>
    <groupId>org.freemarker</groupId>
     <artifactId>freemarker</artifactId>
     <version>2.3.29</version>
 </dependency>

基本指令

  1. FTL标签:FTL标签实际上是FreeMarker自定义的一种指令,一般以#开头;
  2. java有模板元编程嘛 java模板语法_java有模板元编程嘛_03{name};
  3. 注释: 注释和HTML的注释也很相似, 但是它们使用 <#-- and --> 来标识。 不像HTML注释那样,FTL注释不会出现在输出中(不出现在访问者的页面中), 因为 FreeMarker会跳过它们;

1. if: 同java语法,用于判断

<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>
    Welcome ${user}<#if user == "Big Joe">, our beloved leader</#if>!
  </h1>
  <p>Our latest product:
  <a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>

在这段代码,如果user为Big Joe, 则输出 Welcome Big Joe, our beloved leader!,否则输出 Welcome xxx!
ps: 如果要判断不等于,可使用 !=

2. else: 当if条件为false时执行

3. elseif : 当有多个判断条件时可使用elseif

4. list: 当数据类型为列表时,则使用list遍历

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

其中animal是一个对象,name/price是它的属性

5. #items

当我们想要列表显示示例数据模型中的水果,我们可以这样写:

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

但如果我们没有水果,它仍然会输出一个空的<ul></ul>。为了避免这种情况,我们可以这样写

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

此时,list指令将列表视为一个整体,如果我们有0个水果,那么list中的所有内容都会被略过,就不会再有ul标签了。

6.sep 只有当还有下一项时才会被执行

当我们想要在每个后边加一个分隔符(如逗号),但是最后一个水果后边不加分隔符,我们可以这样写:

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

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

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

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

7. include 在模板中插入其他文件

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

<hr>
<i>
Copyright (c) 2021 <a href="http://www.xx.com">xx 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>

常用内建函数

  • user?html 给出user的HTML转义版本,比如&会有&代替
  • user?upper_case user的值全部转大写
  • animal.name?cap_first animal.name的首字母大写
  • user?length 获取user的长度
  • animals?size 获取animals的个数

针对<#list animals as animal></#list>中

  • animal?index 获取animal在animals中的索引,索引从0开始
  • animal?counter 获取animal在animals中的索引,索引从1开始
  • animal?item_parity 基于当前计数的奇偶性,给出字符串"odd"或“even”。

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

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

内建函数可以链式操作,如user?upper_case?html 会先将user转换为大写,再进行html转义

如何处理不存在变量

对于数据模型来讲,有些变量并不是必传。然而FreeMarker在遇到引用的变量不存在时会直接报错。这样情况下,我们需要明确的告诉FreeMarker当变量为空时要如何处理。

  1. 处理方案1:指定默认值
<h1>Welcome ${user!"visitor"}!</h1>

在上述例子中,user不存在时,模板会用visitor作为user的值输出

  1. 处理方案2:在变量后边加??来询问变量是否存在。将它和if指令合并,那么如果user变量不存在的话将会忽略整个代码
<#if user??><h1>Welcome ${user}!</h1></#if>

对于多级变量,为了防止中间某一级变量不存在而导致FreeMarker抛出异常,我们也可以使用(animals.python.price)!0 或 (animals.python.price)??来处理

demo示例

import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

Map<String, Object> templateData = new HashMap<>();
templateData.putAll(performanceScoreResult);

Template template = freeMarkerConfigurer.getConfiguration().getTemplate("summaryReportTemplate.ftl", "utf-8");
String mailContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, templateData);
<table class="scoreTable" style="margin-top: 15px">
                        <thead>
                        <tr>
                            <th width="22%">产线</th>
                            <th width="15%">页面</th>
                            <th width="15%">站点</th>
                            <th width="20%">基准分</th>
                            <th width="20%">上周</th>
                            <th width="23%">本周</th>
                        </tr>
                        </thead>
                        <tbody  class="subTable">
                        <#list onlineResult?keys as businessKey >
                            <tr>
                                <td rowspan="${onlineResult[businessKey]?size}">${businessKey}</td>
                                <#list onlineResult[businessKey]?keys as pageKey >
                                    <td rowspan="${onlineResult[businessKey][pageKey]?size}">${pageKey}</td>
                                    <#list onlineResult[businessKey][pageKey] as scorePage >
                                        <td >${scorePage.localeName}</td>
                                        <td>${scorePage.baseScore}</td>
                                        <td>${scorePage.lastWeekAvgScore}</td>
                                        <td>${scorePage.curWeekAvgScore}</td>
                                    </#list>
                                </#list>
                            </tr>
                        </#list>
                        </tbody>
                    </table>

本文参考了 http://freemarker.foofun.cn/dgui_template_exp.html