springboot整合freemarker

前言

本篇文章主要介紹的是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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章