本章介绍如何整合FreeMarker,简单的快速入门。
FreeMarker官网 :https://freemarker.apache.org/
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。

其实从上面这张图,我们可以看出,FreeMarker就是通过模板+参数,进行标签替换和输出,得到用户视图的过程。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
spring:
freemarker:
#指定HttpServletRequest的属性是否可以覆盖controller的model的同名项
allow-request-override: false
#req访问request
request-context-attribute: req
#后缀名freemarker默认后缀为.ftl,当然你也可以改成自己习惯的.html
suffix: .ftl
#设置响应的内容类型
content-type: text/html;charset=utf-8
#是否允许mvc使用freemarker
enabled: true
#是否开启template caching
cache: false
#设定模板的加载路径,多个以逗号分隔,默认: [“classpath:/templates/”]
template-loader-path: classpath:/templates/
#设定Template的编码
charset: UTF-8
<html>
<head>
<title>${title}title>
head>
<body>
<h1>${context}h1>
body>
html>

@Controller
@RequestMapping("/hello")
public class HelloController {
@GetMapping("/abc")
public String abc(Model model){
System.out.println("=======================");
model.addAttribute("title","你好");
model.addAttribute("context","欢迎光临");
return "hello"; //返回对应hello.ftl的模板
}
}

到这里,FreeMarker基本上运行成功了。
FreeMarker包含的数据类型有布尔,日期,数值,字符,sequence,hash。
下面demo包含了字符串,数值,布尔和日期。
model.addAttribute("str","abc");
model.addAttribute("num",123);
model.addAttribute("flag",true);
model.addAttribute("mydate",new Date());
模板内容
<h2>数据类型h2>
<h3>字符串 h3>
${str}
<h3>数值 : h3>
${num}
<h3>boolean:h3>
?c表示转换成字符串。当flag为boolean类型时,直接使用$ {flag}是不能输出的<br>
${flag?c} 转成字符串(?c和?string都是转字符串,是一样的) <br>
${flag?string} 转成字符串 <br>
${flag?then('yes','no')} 三目运算<br>
${flag?string('y','n')} 三目运算
<h3>日期h3>
${mydate?date} 格式:yyyy-MM-dd<br>
${mydate?time} 格式:HH:mm:ss<br>
${mydate?datetime} 格式:yyyy-MM-dd HH:mm:ss<br>
${mydate?string("yyyy年MM月dd日 HH:mm:ss")} 自定义格式<br>
输出的结果

@GetMapping("/num")
public String num(Model model){
System.out.println("=======================");
model.addAttribute("money",12003.54);
model.addAttribute("score",100000);
model.addAttribute("taxRate",0.13599);
model.addAttribute("taxRate2",0.6439);
return "num";
}
<h1>numh1>
分数: ${score}<br>
数值转字符串输出:${score?c}<br>
数值转货币输出:${money?string.currency}<br>
税率:${taxRate?c}<br>
税率:${taxRate2?c}<br>
税率百分比(默认会四舍五入): ${taxRate?string.percent}<br>
税率保留2位小数(默认会四舍五入): ${taxRate?string["0.##"]}<br>
税率2保留2位小数(默认会四舍五入): ${taxRate2?string["0.##"]}<br>
输出结果

使用#{expression:format}形式格式化数值
mx: 表示小数部分最少几位,
Mx: 表示小数部分最多几位
例如m1M2, 表示最多一位小数,最多两位小数
<#assign a=3.1415926 b=87 />
#{a;m1M2}<br>
#{b;m1M2}<br>
#{a;m1}<br>
#{a;M2}<br>
#{b;m1}<br>
#{b;M2}<br>

FreeMarker支持声明变量,也支持数学运算。
<#assign a=1, b=2 /> 可以声明多个变量,可以使用逗号隔开,也可以使用空格分开
${a} 支持声明<br>
${a+b} 支持运算<br>

<#if condition>…
<#elseif condition2>…
<#elseif condition3>…
<#else>…
#if>
condition 布尔表达式
可以包含0个或者多个elseif,但是else或者elseif必须在if的开始和结束标签之间
示例
<#assign score=58 />
<#if score gt 80 >
${score} 是 优秀
<#elseif score gt 60 >
${score} 是 及格
<#else >
${score} 是 不及格
#if>
<br>
<#if score=58>考了58#if>

FreeMarker标签中,
gt: 大于
lt: 小于
等于直接用 “=”
或者也可以使用小括号,将表达式括起来,再使用>和< ,如下示例
<#assign score=58 />
<#if (score > 80) >
${score} 是 优秀
<#elseif (score > 60) >
${score} 是 及格
<#else >
${score} 是 不及格
#if>
<#switch value>
<#case refValue>…<#break>
<#case refValue>…<#break>
<#default>…
#switch>
switch规则很简单
<#assign month=8 />
${month}月 是
<#switch month>
<#case 1>
<#case 2>
<#case 3>
春天
<#break >
<#case 4>
<#case 5>
<#case 6>
夏天
<#break >
<#case 7>
<#case 8>
<#case 9>
秋天
<#break >
<#case 10>
<#case 11>
<#case 12>
冬天
<#break >
<#default >
错了
#switch>
输出结果

import指令,从其它文件中导入,然后在当前文件中使用。
格式
<#import "params.ftl" as params> 将params.ftl文件导入进来,封装到params对象中
示例
params.ftl文件
<#assign a=1 b=2>
另一个文件中导入它
<#import "params.ftl" as params>
${params.a+params.b} 从其它文件引入变量进行计算
运行结果

类似java里的string处理
@GetMapping("/str")
public String str(Model model){
System.out.println("=======================");
model.addAttribute("str","hello hello world");
return "str";
}
<html>
<head>
<title>stringtitle>
head>
<body>
<h1>Stringh1>
原始字符串:${str}<br>
${str?length} 字符串长度<br>
${str?substring(2)} 从下标2(含)开始到结尾<br>
${str?substring(2,4)} 从下标2(含)开始到下标4(不含)<br>
${str?uncap_first} > 首字母小写<br>
${str?cap_first} 首字母大写<br>
${str?upper_case} 全大写<br>
${str?lower_case} 全小写<br>
${str?starts_with("he")?string} 是否以指定字符开始<br>
${str?ends_with("world")?string} 是否以指定字符结束<br>
${str?index_of("w")?string} 指定字符串的下标<br>
A${" a b c "?trim}B 去除前后的空格<br>
${str?replace("h","H")} 替换字符串<br>
${str?contains("wor")?string("yes","no")} 是否包含某字符串<br>
<br>
body>
html>
输出结果

FreeMarker变量不能为null。
model.addAttribute("str1",null);//会报错
model.addAttribute("str2","");//不会报错
设置字符串为null时会报错

@GetMapping("/nullstr")
public String nullstr(Model model){
System.out.println("=======================");
model.addAttribute("str1",null);//会报错
model.addAttribute("str2","");//不会报错
return "nullstr";
}
<html>
<head>
<title>stringtitle>
head>
<body>
<h1>空字符的问题h1>
${str2}<br>
${str1!} 如果str为null值,则显示空字符串<br>
${(str1??)?string} 判断是否是字符串,返回布尔值。str1=null<br>
${(str2??)?string} 判断是否是字符串,返回布尔值。str2=""<br>
${str1!"默认值"} 字符串为null时,显示自己定义的值<br>
body>
html>
输出结果

model.addAttribute("flag",true);
${flag?then('yes','no')} 三目运算<br>
${flag?string('y','n')} 三目运算
两种写法都可以三目运算,没区别。
User user=new User("小王",20);
model.addAttribute("user",user);
Map<String,String> map=new HashMap<>();
map.put("beijing","北京");
map.put("wuhan","武汉");
map.put("shanghai","上海");
model.addAttribute("mymap",map);
<html>
<head>
<title>objtitle>
head>
<body>
<h1>对象和maph1>
<h3>对象h3>
姓名:${user.name} 从对象里取值$ {obj.fieldName}<br>
年龄:${user.age}<br>
<hr>
<h3>maph3>
${mymap.wuhan} 从对象里取值$ {map.key}<br>
body>
html>
输出结果

相当于java里的数组,List,Set等集合
这里的User类包含了name和age,在下面的集合遍历中用到。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private int age;
}
构建的数据
@GetMapping("/sequence")
public String sequence(Model model){
System.out.println("=======================");
List<User> users=new ArrayList<>();
users.add(new User("小王",12));
users.add(new User("小李",13));
users.add(new User("小刘",11));
users.add(new User("小张",18));
model.addAttribute("users",users);
String[] arr={"bb","aa","cc"};
model.addAttribute("myarray",arr);
Set<String> set=new HashSet<>();
set.add("苹果");
set.add("梨");
set.add("葡萄");
model.addAttribute("fruits",set);
return "sequence";
}
模板sequence.ftl
<html>
<head>
<title>集合遍历title>
head>
<body>
<h1>集合h1>
<hr>
<h3>集合遍历h3>
users as user<br>
<table border="1" width="30%" style="text-align:center;">
<thead><td>index下标td><td>姓名td><td>年龄td>thead>
<tbody>
<#list users as user>
<tr><td>${user?index}td><td>${user.name}td><td>${user.age}td>tr>
#list>
tbody>
<tfoot>
<td>总人数td><td colspan="10">${users?size}td>
tfoot>
table>
获取第一个元素: ${users?first.name}<br>
获取最后一个元素: ${users?last}<br>
总数: ${users?size}<br>
<hr>
<h3>排序h3>
<h4>倒序h4>
users?reverse as user<br>
<table border="1" width="30%" style="text-align:center;">
<thead><td>index下标td><td>姓名td><td>年龄td>thead>
<tbody>
<#list users?reverse as user>
<tr><td>${user?index}td><td>${user.name}td><td>${user.age}td>tr>
#list>
tbody>
table>
<h4>升序h4>
users?sort as user<br>
英文内容的排序,按照字母顺序排序,中文内容的排序按照对应的拼音字母顺序排序<br>
<table border="1" width="30%" style="text-align:center;">
<thead><td>index下标td><td>姓名td><td>年龄td>thead>
<tbody>
<#list users?sort as user>
<tr><td>${user?index}td><td>${user.name}td><td>${user.age}td>tr>
#list>
tbody>
table>
<h4>按字段排序h4>
users?sort_by("age") as user<br>
<table border="1" width="30%" style="text-align:center;">
<thead><td>index下标td><td>姓名td><td>年龄td>thead>
<tbody>
<#list users?sort_by("age") as user>
<tr><td>${user?index}td><td>${user.name}td><td>${user.age}td>tr>
#list>
tbody>
table>
<h4>降序h4>
users?sort?reverse as user<br>
语法里只有升序,没有降序。降序需要依靠升序+倒序。
<br>
<table border="1" width="30%" style="text-align:center;">
<thead><td>index下标td><td>姓名td><td>年龄td>thead>
<tbody>
<#list users?sort?reverse as user>
<tr><td>${user?index}td><td>${user.name}td><td>${user.age}td>tr>
#list>
tbody>
table>
<hr>
<h1>数组h1>
数组和list是一样的,都可以进行遍历,排序,获取长度等,下面只列举了遍历操作
<br>
<#list myarray as a>
${a}<br>
#list>
<hr>
<h1>Seth1>
set和list是一样的,都可以进行遍历,排序,获取长度等,下面只列举了遍历操作
<br>
<#list fruits as f>
${f}<br>
#list>
body>
html>
运行查看结果


类似于 java 中的 Map 类型
@GetMapping("/hash")
public String hash(Model model){
System.out.println("=======================");
Map<String,String> map=new HashMap<>();
map.put("WX","微信");
map.put("QQ","扣扣");
map.put("FB","Facebook");
model.addAttribute("chats",map);
return "hash";
}
模板hash.ftl
<html>
<head>
<title>Hashtitle>
head>
<body>
<h1>Hashh1>
<h3>遍历hashh3><br>
<#list chats?keys as k>
${k} ===${chats[k]}<br>
#list>
<h3>遍历valueh3><br>
<#list chats?values as v>
${v}<br>
#list>
长度: ${chats?size}<br>
body>
html>
运行查看结果
