一、装配使用
1、导入依赖
<!--SpringBootFreemarker模板依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2、在properties里进行配置
##Freemarker配置
##自定义模板文件配置路径默认模板路径在resources/templates下,默认后缀.ftl
##spring.freemarker.template-loader-path=classpath:/web/
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.request-context-attribute=request
spring.freemarker.suffix=.ftl
3、编写controller
package com.tcc.controller;
import java.util.Date;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TestController {
@RequestMapping("f1")
public String test1(ModelMap map) {
//存值
map.addAttribute("flag", true);
map.addAttribute("date", new Date());
map.addAttribute("a",5.5474);
map.addAttribute("b",18);
map.addAttribute("c",10000);
map.addAttribute("t1","Hello");
map.addAttribute("t2", "world");
map.addAttribute("t3", "hello world");
//跳转页面
return "f1";
}
}
4、在src/main/resource目录下创建文件夹templates存放.ftl后缀的页面
二、内建函数
1、后台传入数据跳转页面
package com.tcc.controller;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TestController {
/**
* 访问路径:localhost:8080/f1
* @param map
* @return 返回给页面的数据
*/
@RequestMapping("f1")
public String test1(ModelMap map) {
//布尔类型
map.addAttribute("flag", true);
//日期类型
map.addAttribute("date", new Date());
//数字类型
map.addAttribute("a",5.5474);
map.addAttribute("b",18);
map.addAttribute("c",10000);
//字符串
map.addAttribute("t1","Hello");
map.addAttribute("t2", "world");
map.addAttribute("t3", "hello world");
// 空字符串
map.addAttribute("test1", "");
map.addAttribute("test2", null);
//数组类型
String[] st = {"北京","上海","广东","深圳"};
map.addAttribute("st",st);
//集合类型
List<String> cityList = new ArrayList<>();
cityList.add("北京");
cityList.add("上海");
cityList.add("广东");
cityList.add("深圳");
map.addAttribute("cityList",cityList);
//map类型
Map<String,Object> map2 = new HashMap<>();
map2.put("name", "张三");
map2.put("age", 22);
map2.put("sex", "男");
map.addAttribute("map",map2);
//跳转页面
return "f1";
}
}
2、前台获取不同类型的值
- 只列举了一部分方法,更多方法请查看官网:http://freemarker.foofun.cn/ref_builtins_string.html
<!--
注释(查看网页源代码可以看到) 访问路径:localhost:8080/f1
-->
<#--
注释(前端看不到)
-->
<#-- 布尔类型的值不能直接接收,要在后面添加 ?c 或 ?string 或 ?string('yes','no') -->
<h3>布尔值</h3>
${flag?c} <br/>
${flag?string} <br/>
${flag?string("yes","no")} <br/>
<hr/>
<#-- 日期类型的值不能直接接收,要在后面添加 ?date 或 ?time 或 ?datetime 或 ?string('yyyy/MM/dd') -->
<h3>日期</h3>
${date?date} <br/>
${date?time} <br/>
${date?datetime} <br/>
${date?string("yyyy/MM/dd HH:mm:ss")} <br/>
<hr/>
<#--
数字类型的值可以直接接收,也可以转换类型
a:5.5474 b:18 c:10000
-->
<h3>数字</h3>
${a} <br/>
${a?c} <br/> <#-- 转为字符串 -->
${a?floor} <br/> <#-- 返回最近的整数。 如果数字以.5结尾,那么它将进位 -->
${a?round} <br/> <#-- 返回数字的舍掉小数后的整数 (也就是向负无穷舍弃) -->
${a?ceiling} <br/> <#-- 返回数字小数进位后的整数 (也就是向正无穷进位) -->
${c?string.currency} <br/> <#-- 货币类型 -->
${a?string["0.#"]} <br/> <#-- #小数位占位符,几个#代表保留小数点后几位 -->
<hr/>
<#--
字符串内建函数 t1:Hello t2:world t3:hello world
-->
<h3>字符串</h3>
${t1} -- ${t2} <br/>
${t2?cap_first} <br/> <#-- 首字母大写 -->
${t3?capitalize} <br/> <#-- 全部单词首字母大写 -->
${t1?ends_with("o")?c} <br/> <#-- 返回是否这个字符串以参数中指定的子串结尾 返回值为true或false 需要转换 -->
${t1?starts_with("o")?c} <br/> <#-- 返回是否这个字符串以参数中指定的子串开头 返回值为true或false 需要转换 -->
${t3?length} <br/> <#-- 字符串中字符的数量。 -->
${t1?lower_case} <br/> <#-- 全部小写。 -->
${t1?upper_case} <br/> <#-- 全部大写。 -->
<hr/>
<h3>空字符串[特殊]</h3>
<#--
test1:"" test2:null
test1不报错,test2报错
解决办法:在后面加上[!] 或 [!"为空时候的默认值"]
-->
${test1}
${test2!}
${test2!"yes"} <br/>
${test2!1} <br/>
<hr/>
<#--
序列内建函数:数组(st)、集合(list)、map(map)
-->
<h3>数组</h3>
${st?join(", ")} <br/> <#-- 使用给定的分隔符来连接序列中的项为一个独立的字符串 -->
${st?first} <br/> <#-- 获取第一个值 -->
${st?last} <br/> <#-- 获取最后一个值 -->
<#--
序列中子变量的数量(作为数字值)。
假设序列中至少有一个子变量, 那么序列 s 中最大的索引是 s?size - 1
(因为第一个子变量的序列是0)。
-->
${st?size} <br/>
<#-- 辨别序列中是否包含指定值。它包含一个参数,就是来查找的值。 -->
${st?seq_contains("北京")?string("yes","no")} <br/>
<#list st?reverse as s> <#-- 序列的反序形式。 -->
${s}
</#list>
<br/>
<#--
以升序方式返回序列。(要使用降序排列时,使用它之后使用 reverse 内建函数。)
这仅在子变量都是字符串时有效,或者子变量都是数字,
或者子变量都是日期值 (日期,时间,或日期+时间),
或者所有子变量都是布尔值时(从2.3.17版本开始)。
如果子变量是字符串,它使用本地化(语言)的具体单词排序(通常是大小写不敏感的)。
-->
<#list st?sort as s>
${s}
</#list>
<br/>
<#-- 降序输出 -->
<#list st?sort?reverse as s>
${s}
</#list>
<hr/>
<h3>集合</h3>
<#-- 可用方法和上面举例一样 -->
${cityList?join(", ")} <br/>
${cityList?size} <br/>
<#list cityList as ct>
${ct}
</#list>
<br/>
<#list cityList?sort as ct>
${ct}
</#list>
<hr/>
<h3>Map集合</h3>
<#-- key遍历输出 -->
<#list map?keys as key>
${key} -- ${map[key]} <br/>
</#list>
<br/>
<#-- value遍历输出 -->
<#list map?values as value>
${value} <br/>
</#list>
三、常用指令
1、assign定义变量
1.1、概念
使用该指令你可以创建一个新的变量, 或者替换一个已经存在的变量。注意仅仅顶级变量可以被创建/替换 (也就是说你不能创建/替换 some_hash.subvar
, 除了 some_hash
)。
1.2、使用
- 变量
seq
存储一个序列:
<#assign seq = ["foo","bar","baz"]> <#-- 创建变量 -->
${seq?join(", ")} <#-- 遍历变量 -->
- 一次定义多个变量
<#assign
seq = ["foo", "bar", "baz"]
x++
>
- 变量中插入变量
<#assign z = "张${username}">
2、if,else,elseif
2.1、概念
相当于java中的if,else结构, if
中可以包含任意数量的 elseif
(包括0个) 而且结束时 else
是可选的
2.2、用法一
- 只有
if
没有elseif
和else
:
<#if x == 1>
x is 1
</#if>
- 只有
if
没有elseif
但是有else
:
<#if x == 1>
x is 1
<#else>
x is not 1
</#if>
- 有
if
和两个elseif
但是没有else
:
<#if x == 1>
x is 1
<#elseif x == 2>
x is 2
<#elseif x == 3>
x is 3
</#if>
- 有
if
和三个elseif
还有else
:
<#if x == 1>
x is 1
<#elseif x == 2>
x is 2
<#elseif x == 3>
x is 3
<#elseif x == 4>
x is 4
<#else>
x is not 1 nor 2 nor 3 nor 4
</#if>
- 也可以嵌套
if
指令:
<#if x == 1>
x is 1
<#if y == 1>
and y is 1 too
<#else>
but y is not
</#if>
<#else>
x is not 1
<#if y < 0>
and y is less than 0
</#if>
</#if>
- 测试:
<#assign score = 80>
<#if score lt 60>
<h5>不及格</h5>
<#elseif score = 60>
<h5>不多不少刚刚好</h5>
<#elseif score gt 60 && score lt 80>
<h5>革命尚未成功,同志仍需努力</h5>
<#else>
<h5>考的不错呦</h5>
</#if>
- 注意: 当你想测试是否
x > 0
或x >= 0
,编写<#if x > 0>
和<#if x >= 0>
是错误的, 因为第一个>
会结束#if
标签。要这么来做,可以编写<#if x gt 0>
或<#if gte 0>
。也请注意,如果比较发生在括号内部,那么就没有这样的问题, 比如<#if foo.bar(x > 0)>
就会得到想要的结果。
2.3、用法二
- 根据变量有无值显示内容。
- 测试:
<#-- 有值 -->
<#assign a = "">
<#if a??>
<h5>a有值</h5>
<#else>
<h5>a没有值</h5>
</#if>
<#-- 无值 -->
<#if a1??>
<h5>a1有值</h5>
<#else>
<h5>a1没有值</h5>
</#if>
- 结果:
3、list,else
遍历数组或集合或map。
- 使用:
<#-- 数组有值 -->
<#assign num = [1,2,3,5,4]>
<#list num as n>
${n}
</#list>
<#-- 数组无值 -->
<#assign num1 = []>
<#list num1 as n>
${n}
<#else>
<h5>该集合无值</h5>
</#list>
- 结果:
4、macro自定义指令
4.1、概念
宏变量存储模板片段(称为宏定义体)可以被用作 自定义指令。 这个变量也存储自定义指令的被允许的参数名。当你将这个变量作为指令时, 你必须给所有参数赋值,除了有默认值的参数。 默认值当且仅当你调用宏而不给参数赋值时起作用。
4.2、写法
<#macro name param1 param2 ... paramN>
...
<#nested loopvar1, loopvar2, ..., loopvarN>
...
<#return>
...
</#macro>
-
name
: 宏变量的名称,它不是表达式。和 顶层变量 的语法相同,比如myMacro
或my\-macro
。 然而,它可以被写成字符串的形式,如果宏名称中包含保留字符时,这是很有用的, 比如<#macro "foo~bar">...
。 注意这个字符串没有扩展插值(如"${foo}"
)。 -
param1
,param2
,等…: 局部变量 的名称,存储参数的值 (不是表达式),在=
号后面和默认值(是表达式)是可选的。 默认值也可以是另外一个参数,比如<#macro section title label=title>
。参数名称和 顶层变量 的语法相同,所以有相同的特性和限制。 -
paramN
, 最后一个参数,可能会有三个点(...
), 这就意味着宏接受可变数量的参数,不匹配其它参数的参数可以作为最后一个参数 (也被称作笼统参数)。当宏被命名参数调用,paramN
将会是包含宏的所有未声明的键/值对的哈希表。当宏被位置参数调用,paramN
将是额外参数的序列。 (在宏内部,要查找参数,可以使用myCatchAllParam?is_sequence
。) -
loopvar1
,loopvar2
等…: 可选的,循环变量 的值, 是nested
指令想为嵌套内容创建的。这些都是表达式。
return
和 nested
指令是可选的,而且可以在 <#macro ...>
和 </#macro> 之间被用在任意位置和任意次数。
没有默认值的参数必须在有默认值参数 (paramName=defaultValue
) 之前。
4.3、用法
- 创建简单的自定义指令
<#-- 简单的自定义指令 -->
<#macro test>
This is Test
</#macro>
<#-- 调用指令 -->
<@test/>
- 结果:
- 乘法表(自定义指令中包含内置指令)
<#-- 乘法表(自定义指令中包含内置指令) -->
<#macro cfb>
<#list 1..9 as i>
<#list 1..i as j>
${i} * ${j} = ${i*j}
</#list>
<br/>
</#list>
</#macro>
<@cfb/>
- 结果:
- 可以传参的乘法表
<#-- 可以传参的乘法表 -->
<#macro cfb02 num>
<#list 1..num as i>
<#list 1..i as j>
${i} * ${j} = ${i*j}
</#list>
<br/>
</#list>
</#macro>
<#-- 5,5乘法表 -->
<@cfb02 num=5/>
- 结果:
- 有参数和默认值参数的宏
<#-- 有参数和默认值参数的宏 -->
<#macro cfb02 num=9>
<#list 1..num as i>
<#list 1..i as j>
${i} * ${j} = ${i*j}
</#list>
<br/>
</#list>
</#macro>
<#-- 5,5乘法表 -->
<@cfb02 num=5/>
<#-- 9,9乘法表,默认num=9 -->
<@cfb02/>
- 结果:
4.4、nested占位符
nested
指令执行自定义指令开始和结束标签中间的模板片段。 嵌套的片段可以包含模板中任意合法的内容:插值,指令等…它在上下文环境中被执行, 也就是宏被调用的地方,而不是宏定义体的上下文中。因此,比如, 你不能看到嵌套部分的宏的局部变量。如果你没有调用 nested
指令, 自定义指令开始和结束标记中的部分将会被忽略。
使用
<#-- nested占位符 -->
<#macro test>
This is Test
<#nested>
</#macro>
<#-- 调用指令 -->
<@test>xxxx</@test>
- 结果:
4.5、注意
- 变量会在模板开始时被创建;而不管
macro
指令放置在模板的什么位置。所以这样也可以:
<#-- 调用指令 -->
<@test/>
<#-- 简单的自定义指令 -->
<#macro test>
This is Test
</#macro>
- 如果宏定义被插在
include
指令中, 它们直到 FreeMarker 执行include
指令时才会可用。
5、import
5.1、概念
引入一个库。也就是说,它创建一个新的空命名空间, 然后在那个命名空间中执行给定 path
参数中的模板, 所以模板用变量(宏,函数等)填充命名空间。 然后使得新创建的命名空间对哈希表的调用者可用。 这个哈希表变量将会在命名空间中,由 import
(就像你可以用 assign
指令来创建一样。) 的调用者被创建成一个普通变量,名字就是 hash
参数给定的。
5.2、写法
<#import path as hash>
-
path
:模板的路径。 这是一个算作是字符串的表达式。(换句话说,它不是一个固定的字符串, 它可以是这样的一些东西,比如,profile.baseDir + "/menu.ftl"
。) -
hash
: 访问命名空间的哈希表变量不带引号的名字。不是表达式。 (如果要引入动态创建的名字,那么就不得不使用 这个技巧。)
5.3、用法
- 在controller写跳转
f3.ftl
页面的请求路径
@RequestMapping("f3")
public String test2(ModelMap map) {
return "f3";
}
- 创建commons.ftl用于存放公共的自定义指令
- 在测试页面中调用公共指令
<#import "commons.ftl" as cm> <br/>
<@cm.test/> <br/>
<@cm.cfb/> <br/>
<@cm.cfb02 num=8/>
- 结果:
6、include包含
6.1、概念
你可以使用它在你的模板中插入另外一个 FreeMarker 模板文件 (由 path
参数指定)。 被包含模板的输出格式是在 include
标签出现的位置插入的。 被包含的文件和包含它的模板共享变量,就像是被复制粘贴进去的一样。 include
指令不能由被包含文件的内容所替代, 它只是当 FreeMarker 每次在模板处理期间到达 include
指令时处理被包含的文件。所以对于如果 include
在 list
循环之中的例子, 你可以为每个循环周期内指定不同的文件名。
6.2、写法
<#include path>
或
<#include path options>
-
path
: 要包含文件的路径;一个算作是字符串的表达式。(用其他话说, 它不用是一个固定的字符串,它也可以是像profile.baseDir + "/menu.ftl"
这样的东西。) -
options
: 一个或多个这样的选项:encoding=encoding
,parse=parse
-
encoding
: 算作是字符串的表达式 -
parse
: 算作是布尔值的表达式(为了向下兼容,也接受一部分字符串值) -
ignore_missing
: 算作是布尔值的表达式
6.3、用法
- 创建
.ftl、.html、.txt
文件,里面输入任意内容。 - 在测试页面输入指令测试
<#-- 引用test.txt里面的内容 -->
<#include "test.txt"> <br/>
<#-- 引用test.html里面的内容 -->
<#include "test.html"> <br/>
<#-- 引用test.ftl里面的内容 -->
<#include "test.ftl"> <br/>
- 结果:
具体使用请查看官方文档:FreeMarker官方文档。