前言
本篇文章主要介紹的是springboot整合freemarker填充ftl模板文件摸恍,生成新的文件(如html),以及freemarker的語法赤屋。
GitHub源碼鏈接位于文章底部立镶。
freemarker介紹
freemarker是一款模板引擎,它基于模板來生成文本輸出类早。這里的文本包括但不限于html頁面媚媒,word,各種源代碼文本......
工作原理
模板:就是一份已經(jīng)寫好了基本內(nèi)容涩僻,有著固定格式的文檔缭召,其中空出或者用占
位符標(biāo)識(shí)的內(nèi)容栈顷,由使用者來填充,不同的使用者給出的數(shù)據(jù)是不同的嵌巷。在模板
中的占位符萄凤,在模板運(yùn)行時(shí),由模板引擎來解析模板搪哪,并采用動(dòng)態(tài)數(shù)據(jù)替換占位
符部分的內(nèi)容靡努。
freemarker的應(yīng)用方向有兩個(gè),一是基于ftl文件晓折,將內(nèi)容填充到ftl文件中颤难,就可以使用制作ftl模板的文本的方式進(jìn)行訪問和顯示了,比如使用html文本制作了一個(gè)ftl模板已维,我們使用代碼填充數(shù)據(jù)進(jìn)ftl模板行嗤,那么我們就能以訪問html的方式去打開這個(gè)文件了;另一種方式則是直接生成對(duì)應(yīng)的文件垛耳,比如生成xxx.html的文件栅屏。
應(yīng)用場(chǎng)景:
淘寶中的商品數(shù)不勝數(shù),在商品的詳情頁這一塊堂鲜,如果全都以真實(shí)的html頁面顯示栈雳,那么有多少個(gè)商品就得有多少個(gè)頁面了,何況還有增刪改的情況缔莲。所以使用一個(gè)固定的ftl模板哥纫,填充數(shù)據(jù),這樣一個(gè)文件就能顯示無數(shù)個(gè)頁面的內(nèi)容了痴奏。
再比如一些政府單位的項(xiàng)目蛀骇,每天需要發(fā)送一些word文檔給領(lǐng)導(dǎo),此時(shí)只需要通過程序?qū)?shù)據(jù)填充進(jìn)ftl模板读拆,然后生成一個(gè)個(gè)的xxx.word文件就行了擅憔。
ftl指令
1.一些常見的符號(hào)說明:
${}插值; 只能輸出數(shù)值檐晕、日期或者字符串暑诸,其它類型不能輸出。
在ftl頁面中添加如下代碼:
<#--這是 freemarker 注釋辟灰,不會(huì)輸出到文件中-->
<h1>${name}个榕; ${message}</h1>
在freemarker接口里添加數(shù)據(jù)如下:
這一段代碼在頁面輸出的就是:
<#freemarker 命令
<#-- 注釋 -->
<@使用自定義命令
??是判斷對(duì)象是否存在
?函數(shù)調(diào)用
2.assign
此指令用于在頁面上定義一個(gè)變量。 語法: <#assign name=value>
<#--assign-->
<#--簡(jiǎn)單類型-->
<#assign linkman="李四"/>
聯(lián)系人:${linkman}
<#assign info={"tel":"110","sex":"男"}/>
電話:${info.tel};性別:${info.sex}
頁面輸出如下:
3. include
語法: <#include path>
說明: path 參數(shù)可以是如 "foo.ftl"和 "../foo.ftl"一樣的相對(duì)路徑芥喇,或者是如"/foo.ftl"這樣的絕對(duì)路徑西采。
創(chuàng)建模板文件 header.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>嵌入頁面</title>
</head>
<body>
<h1>嵌入頁面</h1>
</body>
</html>
在index模板文件中使用 include 指令引入 header.ftl 模板文件
<#--include-->
<#include "header.ftl"/>
訪問接口,在index.ftl視圖如下:
4.if
<#if condition>
...
<#elseif condition2>
...
<#elseif condition3>
...
...
<#else>
...
</#if>
這里:
condition 乃坤, condition2 等:表達(dá)式將被計(jì)算成布爾值苛让。
關(guān)鍵字: gt :比較運(yùn)算符“大于”沟蔑; gte :比較運(yùn)算符“大于或等于”; lt :比較
運(yùn)算符“小于”狱杰; lte :比較運(yùn)算符“小于或等于”
<#--if-->
<#assign bool=false/>
<#if bool>
bool為true
<#else>
bool為false
</#if>
這里定義一個(gè)bool為false瘦材,然后使用if進(jìn)行判斷刃榨,最后輸出為false车要,
頁面顯示為:bool為false
5.list
語法:
<#list sequence as item>
...
</#list>
這里:
sequence :表達(dá)式將被算作序列或集合
item :循環(huán)變量(不是表達(dá)式)的名稱
如果想在循環(huán)中得到索引,使用循環(huán)變量+_index 就可以得到馁痴。如上述語法中則可以使用 item_index 可以得到循環(huán)變量
在freemarker接口里添加數(shù)據(jù)如下:
在index.ftl中遍歷
<#--list-->
<#list goodsList as fruit>
索引:${fruit_index},
水果:${fruit.name};
價(jià)格:${fruit.price}
</#list>
頁面顯示為:
6.內(nèi)建函數(shù)
6.1使用 size 函數(shù)來實(shí)現(xiàn)對(duì)于集合大小的獲却砉痢:
<#--獲取集合總記錄數(shù)-->
總共${goodsList?size}條記錄
<#--集合中索引為1的name屬性-->
${goodsList[1].name}
<#--集合中第一個(gè)元素的name屬性-->
${goodsList?first.name}
顯示如下:“總共2條記錄 香蕉 蘋果 ”
6.2可以使用 eval 將 json 字符串轉(zhuǎn)換為對(duì)象
<#assign str="{'id':'123','text':'文本'}"/>
<#assign jsonObj=str?eval/>
id為:${jsonObj.id};text為:${jsonObj.text}
顯示如下:“id為:123;text為:文本”
6.3日期格式化
在freemarker接口里添加數(shù)據(jù)如下:
dataModel.put("today", new Date());
ftl頁面添加:
當(dāng)前日期:${today?date}<br>
<hr>
當(dāng)前時(shí)間:${today?time}<br>
<hr>
當(dāng)前日期+時(shí)間:${today?datetime}
<hr>
格式化顯示當(dāng)前日期時(shí)間:${today?string('yyyy年MM月dd日 HH:mm:ss')}
顯示如下:
6.4數(shù)字轉(zhuǎn)換為字符串
在freemarker接口里添加數(shù)據(jù)如下:
dataModel.put("number", 123456789L);
index.ftl模板中添加:
<#--數(shù)值顯示處理-->
${number}
顯示如下:“123,456,789”
發(fā)現(xiàn)數(shù)字會(huì)以每三位一個(gè)分隔符顯示簿晓,有些時(shí)候不需要這個(gè)分隔符,就需要將數(shù)
字轉(zhuǎn)換為字符串,使用內(nèi)建函數(shù) c
${number?c}
顯示如下:“123456789”
7.空值處理
在 FreeMarker 中對(duì)于空值必須手動(dòng)處理千埃。
{emp.name!(“名字為空”)} 表示 name 為空時(shí)顯示 名字為空
{bool???string} 表示:首先??表示判斷 bool 變量是否存在谒臼,存在返回 true 否則 false,然后對(duì)返回的值調(diào)用其內(nèi)置函數(shù) string
<#if str??> 表示去判斷 str 變量是否存在耀里,存在則 true蜈缤,不存在為 false
${strs!"strs空值的默認(rèn)顯示值"}
<hr>
<#if strs??>
str變量存在
<#else >
strs變量不存在
</#if>
顯示如下:
8.運(yùn)算符
8.1算數(shù)運(yùn)算符
FreeMarker 表達(dá)式中完全支持算術(shù)運(yùn)算,FreeMarker 支持的算術(shù)運(yùn)算符包括:+, - ,
*, / , %
8.2邏輯運(yùn)算符
邏輯運(yùn)算符有如下幾個(gè):
邏輯與:&&
邏輯或:||
邏輯非:!
邏輯運(yùn)算符只能作用于布爾值,否則將產(chǎn)生錯(cuò)誤
8.3. 比較運(yùn)算符
表達(dá)式中支持的比較運(yùn)算符有如下幾個(gè):
1 =或者==:判斷兩個(gè)值是否相等.
2 !=:判斷兩個(gè)值是否不等.
3 >或者 gt:判斷左邊值是否大于右邊值
4 >=或者 gte:判斷左邊值是否大于等于右邊值
5 <或者 lt:判斷左邊值是否小于右邊值
6 <=或者 lte:判斷左邊值是否小于等于右邊值
=和!=可以用于字符串,數(shù)值和日期來比較是否相等,但=和!=兩邊必須是相
同類型的值,否則會(huì)產(chǎn)生錯(cuò)誤,而且 FreeMarker 是精確比較,"x","x ","X"
是不等的.其
它的運(yùn)行符可以作用于數(shù)字和日期,但不能作用于字符串,大部分的時(shí)候,使用 gt
等字母運(yùn)算符代替>會(huì)有更好的效果,因?yàn)?FreeMarker 會(huì)把>解釋成 FTL 標(biāo)簽的結(jié)
束字符,當(dāng)然,也可以使用括號(hào)來避免這種情況,如:<#if (x>y)>
springboot整合freemarker
首先來看一下項(xiàng)目結(jié)構(gòu):
創(chuàng)建一個(gè)springboot項(xiàng)目
pom文件中添加依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
application.yml中添加freemarker的相關(guān)配置
server:
port: 8080
spring:
freemarker:
#設(shè)置編碼格式
charset: utf-8
#設(shè)置文件后綴
suffix: .ftl
#設(shè)置ftl文件路徑
template-loader-path: classpath:/templates
#關(guān)閉緩存,及時(shí)刷新冯挎,上線生產(chǎn)環(huán)境需要改為true
cache: false
在resources目錄中添加templates文件夾因?yàn)檫@在yml中配置了底哥。
template文件夾中添加兩個(gè)ftl模板文件,待會(huì)兒會(huì)用到房官,因?yàn)槭莿?chuàng)建html的文件趾徽,所以直接新建一個(gè)html文件,再將后綴改為ftl就行了易阳。
index.ftl代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Freemarker 測(cè)試</title>
</head>
<body>
<#--這是 freemarker 注釋附较,不會(huì)輸出到文件中-->
<h1>${name}; ${message}</h1>
<hr>
<#--assign-->
<#--簡(jiǎn)單類型-->
<#assign linkman="李四"/>
聯(lián)系人:${linkman}
<#assign info={"tel":"110","sex":"男"}/>
電話:${info.tel};性別:${info.sex}
<hr>
<#--include-->
<#include "header.ftl"/>
<hr>
<#--if-->
<#assign bool=false/>
<#if bool>
bool為true
<#else>
bool為false
</#if>
<hr>
<#--list-->
<#list goodsList as fruit>
索引:${fruit_index},
水果:${fruit.name};
價(jià)格:${fruit.price}<br>
</#list>
<hr>
<#--獲取集合總記錄數(shù)-->
總共${goodsList?size}條記錄
<#--集合中索引為1的name屬性-->
${goodsList[1].name}
<#--集合中第一個(gè)元素的name屬性-->
${goodsList?first.name}
<hr>
<#assign str="{'id':'123','text':'文本'}"/>
<#assign jsonObj=str?eval/>
id為:${jsonObj.id};text為:${jsonObj.text}<p></p>
<hr>
當(dāng)前日期:${today?date}<br>
<hr>
當(dāng)前時(shí)間:${today?time}<br>
<hr>
當(dāng)前日期+時(shí)間:${today?datetime}
<hr>
格式化顯示當(dāng)前日期時(shí)間:${today?string('yyyy年MM月dd日 HH:mm:ss')}
<hr>
<#--數(shù)值顯示處理-->
${number}
<hr>
${number?c}<p></p>
<hr>
${strs!"strs空值的默認(rèn)顯示值"}
<hr>
<#if strs??>
str變量存在
<#else >
strs變量不存在
</#if>
</body>
</html>
header.ftl代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>嵌入頁面</title>
</head>
<body>
<h1>嵌入頁面</h1>
</body>
</html>
在freemarker接口中添加代碼潦俺,返回freemarker的index.ftl視圖
@RequestMapping("/freemarkerIndex")
public String freemarker(Map<String, Object> dataModel) {
dataModel.put("name", "張三");
dataModel.put("message", "hello world");
List<Map<String,Object>> list = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "蘋果");
map1.put("price", 4.5);
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "香蕉");
map2.put("price", 6.3);
list.add(map1);
list.add(map2);
dataModel.put("goodsList", list);
dataModel.put("today", new Date());
dataModel.put("number", 123456789L);
return "index";
}
ps:這里dataModel的map只能從方法參數(shù)中獲取,如果是自己new的去存儲(chǔ)數(shù)據(jù)徐勃,填充到模板中事示,會(huì)報(bào)空值異常。
啟動(dòng)程序僻肖,通過localhost:8080/freemarkerIndex 訪問該接口肖爵,得到頁面如下:
上面這個(gè)接口是直接返回視圖,
接下來是通過同樣的數(shù)據(jù)臀脏,生成index.html靜態(tài)文件劝堪。
因?yàn)檫@里是不用返回的冀自,如果做成接口的話,雖然能成功生成文件秒啦,但是會(huì)報(bào)404熬粗,所以這里用的是單元測(cè)試,實(shí)際開發(fā)中余境,如果是返回的json格式驻呐,而不是視圖,就可以做成接口芳来。
@Test
public void createFileByFreemarker() throws IOException, TemplateException {
//創(chuàng)建配置對(duì)象
Configuration configuration = new Configuration(Configuration.getVersion());
//設(shè)置默認(rèn)生成文件編碼
configuration.setDefaultEncoding("utf-8");
//設(shè)置模板路徑
configuration.setClassForTemplateLoading(this.getClass(), "/templates");
//獲取模板
Template template = configuration.getTemplate("index.ftl");
//加載數(shù)據(jù)
Map<String, Object> dataModel =new HashMap<>();
dataModel.put("name", "張三");
dataModel.put("message", "hello world");
List<Map<String,Object>> list = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "蘋果");
map1.put("price", 4.5);
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "香蕉");
map2.put("price", 6.3);
list.add(map1);
list.add(map2);
dataModel.put("goodsList", list);
dataModel.put("today", new Date());
dataModel.put("number", 123456789L);
//創(chuàng)建輸出對(duì)象,將文件輸出到D盤根目錄下
FileWriter fileWriter = new FileWriter("D:/index.html");
//渲染模板和數(shù)據(jù)
template.process(dataModel, fileWriter);
//關(guān)閉輸出
fileWriter.close();
}
本文GitHub源碼:https://github.com/lixianguo5097/springboot/tree/master/springboot-freemarker
CSDN:https://blog.csdn.net/qq_27682773
簡(jiǎn)書:http://www.reibang.com/u/e99381e6886e
博客園:https://www.cnblogs.com/lixianguo
個(gè)人博客:https://www.lxgblog.com