2、在properties里进行配置org.springframework.boot spring-boot-starter-freemarker
##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=.ftl3、编写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 {
@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 cityList = new ArrayList<>();
cityList.add("北京");
cityList.add("上海");
cityList.add("广东");
cityList.add("深圳");
map.addAttribute("cityList",cityList);
//map类型
Map 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
<#--
注释(前端看不到)
-->
<#-- 布尔类型的值不能直接接收,要在后面添加 ?c 或 ?string 或 ?string('yes','no') -->
布尔值
${flag?c}
${flag?string}
${flag?string("yes","no")}
<#-- 日期类型的值不能直接接收,要在后面添加 ?date 或 ?time 或 ?datetime 或 ?string('yyyy/MM/dd') -->
日期
${date?date}
${date?time}
${date?datetime}
${date?string("yyyy/MM/dd HH:mm:ss")}
<#--
数字类型的值可以直接接收,也可以转换类型
a:5.5474 b:18 c:10000
-->
数字
${a}
${a?c}
<#-- 转为字符串 -->
${a?floor}
<#-- 返回最近的整数。 如果数字以.5结尾,那么它将进位 -->
${a?round}
<#-- 返回数字的舍掉小数后的整数 (也就是向负无穷舍弃) -->
${a?ceiling}
<#-- 返回数字小数进位后的整数 (也就是向正无穷进位) -->
${c?string.currency}
<#-- 货币类型 -->
${a?string["0.#"]}
<#-- #小数位占位符,几个#代表保留小数点后几位 -->
<#--
字符串内建函数 t1:Hello t2:world t3:hello world
-->
字符串
${t1} -- ${t2}
${t2?cap_first}
<#-- 首字母大写 -->
${t3?capitalize}
<#-- 全部单词首字母大写 -->
${t1?ends_with("o")?c}
<#-- 返回是否这个字符串以参数中指定的子串结尾 返回值为true或false 需要转换 -->
${t1?starts_with("o")?c}
<#-- 返回是否这个字符串以参数中指定的子串开头 返回值为true或false 需要转换 -->
${t3?length}
<#-- 字符串中字符的数量。 -->
${t1?lower_case}
<#-- 全部小写。 -->
${t1?upper_case}
<#-- 全部大写。 -->
空字符串[特殊]
<#--
test1:"" test2:null
test1不报错,test2报错
解决办法:在后面加上[!] 或 [!"为空时候的默认值"]
-->
${test1}
${test2!}
${test2!"yes"}
${test2!1}
<#--
序列内建函数:数组(st)、集合(list)、map(map)
-->
数组
${st?join(", ")}
<#-- 使用给定的分隔符来连接序列中的项为一个独立的字符串 -->
${st?first}
<#-- 获取第一个值 -->
${st?last}
<#-- 获取最后一个值 -->
<#--
序列中子变量的数量(作为数字值)。
假设序列中至少有一个子变量, 那么序列 s 中最大的索引是 s?size - 1
(因为第一个子变量的序列是0)。
-->
${st?size}
<#-- 辨别序列中是否包含指定值。它包含一个参数,就是来查找的值。 -->
${st?seq_contains("北京")?string("yes","no")}
<#list st?reverse as s> <#-- 序列的反序形式。 -->
${s}
#list>
<#--
以升序方式返回序列。(要使用降序排列时,使用它之后使用 reverse 内建函数。)
这仅在子变量都是字符串时有效,或者子变量都是数字,
或者子变量都是日期值 (日期,时间,或日期+时间),
或者所有子变量都是布尔值时(从2.3.17版本开始)。
如果子变量是字符串,它使用本地化(语言)的具体单词排序(通常是大小写不敏感的)。
-->
<#list st?sort as s>
${s}
#list>
<#-- 降序输出 -->
<#list st?sort?reverse as s>
${s}
#list>
集合
<#-- 可用方法和上面举例一样 -->
${cityList?join(", ")}
${cityList?size}
<#list cityList as ct>
${ct}
#list>
<#list cityList?sort as ct>
${ct}
#list>
Map集合
<#-- key遍历输出 -->
<#list map?keys as key>
${key} -- ${map[key]}
#list>
<#-- value遍历输出 -->
<#list map?values as value>
${value}
#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>不及格
<#elseif score = 60>不多不少刚刚好
<#elseif score gt 60 && score lt 80>革命尚未成功,同志仍需努力
<#else>考的不错呦
#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??>a有值
<#else>a没有值
#if> <#-- 无值 --> <#if a1??>a1有值
<#else>a1没有值
#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>
该集合无值
#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>
#list>
#macro>
<@cfb/>
结果:
可以传参的乘法表
<#-- 可以传参的乘法表 -->
<#macro cfb02 num>
<#list 1..num as i>
<#list 1..i as j>
${i} * ${j} = ${i*j}
#list>
#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>
#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>
<@cm.test/>
<@cm.cfb/>
<@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=parseencoding: 算作是字符串的表达式parse: 算作是布尔值的表达式(为了向下兼容,也接受一部分字符串值)ignore_missing: 算作是布尔值的表达式 6.3、用法
创建.ftl、.html、.txt文件,里面输入任意内容。
在测试页面输入指令测试
<#-- 引用test.txt里面的内容 --> <#include "test.txt">
<#-- 引用test.html里面的内容 --> <#include "test.html">
<#-- 引用test.ftl里面的内容 --> <#include "test.ftl">
结果:
具体使用请查看官方文档:FreeMarker官方文档。



