前言
本篇文章主要介紹的是springboot整合freemarker填充ftl模板文件,生成新的文件(如html),以及freemarker的語法。
GitHub源碼鏈接位於文章底部。
freemarker介紹
freemarker是一款模板引擎,它基於模板來生成文本輸出。這裏的文本包括但不限於html頁面,word,各種源代碼文本…
工作原理
模板:就是一份已經寫好了基本內容,有着固定格式的文檔,其中空出或者用佔
位符標識的內容,由使用者來填充,不同的使用者給出的數據是不同的。在模板
中的佔位符,在模板運行時,由模板引擎來解析模板,並採用動態數據替換佔位
符部分的內容。
freemarker的應用方向有兩個,一是基於ftl文件,將內容填充到ftl文件中,就可以使用製作ftl模板的文本的方式進行訪問和顯示了,比如使用html文本製作了一個ftl模板,我們使用代碼填充數據進ftl模板,那麼我們就能以訪問html的方式去打開這個文件了;另一種方式則是直接生成對應的文件,比如生成xxx.html的文件。
應用場景:
淘寶中的商品數不勝數,在商品的詳情頁這一塊,如果全都以真實的html頁面顯示,那麼有多少個商品就得有多少個頁面了,何況還有增刪改的情況。所以使用一個固定的ftl模板,填充數據,這樣一個文件就能顯示無數個頁面的內容了。
再比如一些政府單位的項目,每天需要發送一些word文檔給領導,此時只需要通過程序將數據填充進ftl模板,然後生成一個個的xxx.word文件就行了。
ftl指令
1.一些常見的符號說明:
${}插值; 只能輸出數值、日期或者字符串,其它類型不能輸出。
在ftl頁面中添加如下代碼:
<#--這是 freemarker 註釋,不會輸出到文件中-->
<h1>${name}; ${message}</h1>
在freemarker接口裏添加數據如下:
這一段代碼在頁面輸出的就是:
<#freemarker 命令
<#-- 註釋 -->
<@使用自定義命令
??是判斷對象是否存在
?函數調用
2.assign
此指令用於在頁面上定義一個變量。 語法: <#assign name=value>
<#--assign-->
<#--簡單類型-->
<#assign linkman="李四"/>
聯繫人:${linkman}
<#assign info={"tel":"110","sex":"男"}/>
電話:${info.tel};性別:${info.sex}
頁面輸出如下:
3. include
語法: <#include path>
說明: path 參數可以是如 "foo.ftl"和 “…/foo.ftl"一樣的相對路徑,或者是如”/foo.ftl"這樣的絕對路徑。
創建模板文件 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 等:表達式將被計算成布爾值。
關鍵字: gt :比較運算符“大於”; gte :比較運算符“大於或等於”; lt :比較
運算符“小於”; lte :比較運算符“小於或等於”
<#--if-->
<#assign bool=false/>
<#if bool>
bool爲true
<#else>
bool爲false
</#if>
這裏定義一個bool爲false,然後使用if進行判斷,最後輸出爲false,
頁面顯示爲:bool爲false
5.list
語法:
<#list sequence as item>
…
</#list>
這裏:
sequence :表達式將被算作序列或集合
item :循環變量(不是表達式)的名稱
如果想在循環中得到索引,使用循環變量+_index 就可以得到。如上述語法中則可以使用 item_index 可以得到循環變量
在freemarker接口裏添加數據如下:
在index.ftl中遍歷
<#--list-->
<#list goodsList as fruit>
索引:${fruit_index},
水果:${fruit.name};
價格:${fruit.price}
</#list>
頁面顯示爲:
6.內建函數
6.1使用 size 函數來實現對於集合大小的獲取:
<#--獲取集合總記錄數-->
總共${goodsList?size}條記錄
<#--集合中索引爲1的name屬性-->
${goodsList[1].name}
<#--集合中第一個元素的name屬性-->
${goodsList?first.name}
顯示如下:“總共2條記錄 香蕉 蘋果 ”
6.2可以使用 eval 將 json 字符串轉換爲對象
<#assign str="{'id':'123','text':'文本'}"/>
<#assign jsonObj=str?eval/>
id爲:${jsonObj.id};text爲:${jsonObj.text}
顯示如下:“id爲:123;text爲:文本”
6.3日期格式化
在freemarker接口裏添加數據如下:
dataModel.put("today", new Date());
ftl頁面添加:
當前日期:${today?date}<br>
<hr>
當前時間:${today?time}<br>
<hr>
當前日期+時間:${today?datetime}
<hr>
格式化顯示當前日期時間:${today?string('yyyy年MM月dd日 HH:mm:ss')}
顯示如下:
6.4數字轉換爲字符串
在freemarker接口裏添加數據如下:
dataModel.put("number", 123456789L);
index.ftl模板中添加:
<#--數值顯示處理-->
${number}
顯示如下:“123,456,789”
發現數字會以每三位一個分隔符顯示,有些時候不需要這個分隔符,就需要將數
字轉換爲字符串,使用內建函數 c
${number?c}
顯示如下:“123456789”
7.空值處理
在 FreeMarker 中對於空值必須手動處理。
${emp.name!} 表示 name 爲空時什麼都不顯示
${emp.name!(“名字爲空”)} 表示 name 爲空時顯示 名字爲空
${(emp.company.name)!} 表示如果 company 對象爲空則什麼都不顯示, !只用
在最近的那個屬性判斷;所以如果遇上有自定義類型(導航)屬性時,需要使用括號
${bool???string} 表示:首先??表示判斷 bool 變量是否存在,存在返回 true 否則 false,然後對返回的值調用其內置函數 string
<#if str??> 表示去判斷 str 變量是否存在,存在則 true,不存在爲 false
${strs!"strs空值的默認顯示值"}
<hr>
<#if strs??>
str變量存在
<#else >
strs變量不存在
</#if>
顯示如下:
8.運算符
8.1算數運算符
FreeMarker 表達式中完全支持算術運算,FreeMarker 支持的算術運算符包括:+, - ,
*, / , %
8.2邏輯運算符
邏輯運算符有如下幾個:
邏輯與:&&
邏輯或:||
邏輯非:!
邏輯運算符只能作用於布爾值,否則將產生錯誤
8.3. 比較運算符
表達式中支持的比較運算符有如下幾個:
1 =或者==:判斷兩個值是否相等.
2 !=:判斷兩個值是否不等.
3 >或者 gt:判斷左邊值是否大於右邊值
4 >=或者 gte:判斷左邊值是否大於等於右邊值
5 <或者 lt:判斷左邊值是否小於右邊值
6 <=或者 lte:判斷左邊值是否小於等於右邊值
=和!=可以用於字符串,數值和日期來比較是否相等,但=和!=兩邊必須是相
同類型的值,否則會產生錯誤,而且 FreeMarker 是精確比較,"x","x ","X"
是不等的.其
它的運行符可以作用於數字和日期,但不能作用於字符串,大部分的時候,使用 gt
等字母運算符代替>會有更好的效果,因爲 FreeMarker 會把>解釋成 FTL 標籤的結
束字符,當然,也可以使用括號來避免這種情況,如:<#if (x>y)>
####springboot整合freemarker
首先來看一下項目結構:
創建一個springboot項目
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的相關配置
server:
port: 8080
spring:
freemarker:
#設置編碼格式
charset: utf-8
#設置文件後綴
suffix: .ftl
#設置ftl文件路徑
template-loader-path: classpath:/templates
#關閉緩存,及時刷新,上線生產環境需要改爲true
cache: false
在resources目錄中添加templates文件夾因爲這在yml中配置了。
template文件夾中添加兩個ftl模板文件,待會兒會用到,因爲是創建html的文件,所以直接新建一個html文件,再將後綴改爲ftl就行了。
index.ftl代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Freemarker 測試</title>
</head>
<body>
<#--這是 freemarker 註釋,不會輸出到文件中-->
<h1>${name}; ${message}</h1>
<hr>
<#--assign-->
<#--簡單類型-->
<#assign linkman="李四"/>
聯繫人:${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};
價格:${fruit.price}<br>
</#list>
<hr>
<#--獲取集合總記錄數-->
總共${goodsList?size}條記錄
<#--集合中索引爲1的name屬性-->
${goodsList[1].name}
<#--集合中第一個元素的name屬性-->
${goodsList?first.name}
<hr>
<#assign str="{'id':'123','text':'文本'}"/>
<#assign jsonObj=str?eval/>
id爲:${jsonObj.id};text爲:${jsonObj.text}<p></p>
<hr>
當前日期:${today?date}<br>
<hr>
當前時間:${today?time}<br>
<hr>
當前日期+時間:${today?datetime}
<hr>
格式化顯示當前日期時間:${today?string('yyyy年MM月dd日 HH:mm:ss')}
<hr>
<#--數值顯示處理-->
${number}
<hr>
${number?c}<p></p>
<hr>
${strs!"strs空值的默認顯示值"}
<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只能從方法參數中獲取,如果是自己new的去存儲數據,填充到模板中,會報空值異常。
啓動程序,通過localhost:8080/freemarkerIndex 訪問該接口,得到頁面如下:
上面這個接口是直接返回視圖,
接下來是通過同樣的數據,生成index.html靜態文件。
因爲這裏是不用返回的,如果做成接口的話,雖然能成功生成文件,但是會報404,所以這裏用的是單元測試,實際開發中,如果是返回的json格式,而不是視圖,就可以做成接口。
@Test
public void createFileByFreemarker() throws IOException, TemplateException {
//創建配置對象
Configuration configuration = new Configuration(Configuration.getVersion());
//設置默認生成文件編碼
configuration.setDefaultEncoding("utf-8");
//設置模板路徑
configuration.setClassForTemplateLoading(this.getClass(), "/templates");
//獲取模板
Template template = configuration.getTemplate("index.ftl");
//加載數據
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);
//創建輸出對象,將文件輸出到D盤根目錄下
FileWriter fileWriter = new FileWriter("D:/index.html");
//渲染模板和數據
template.process(dataModel, fileWriter);
//關閉輸出
fileWriter.close();
}
本文GitHub源碼:https://github.com/lixianguo5097/springboot/tree/master/springboot-freemarker
CSDN:https://blog.csdn.net/qq_27682773
簡書:https://www.jianshu.com/u/e99381e6886e
博客園:https://www.cnblogs.com/lixianguo
個人博客:https://www.lxgblog.com