springboot整合freemarker

前言

本篇文章主要介紹的是springboot整合freemarker填充ftl模板文件摸恍,生成新的文件(如html),以及freemarker的語法赤屋。

GitHub源碼鏈接位于文章底部立镶。

freemarker介紹

freemarker是一款模板引擎,它基于模板來生成文本輸出类早。這里的文本包括但不限于html頁面媚媒,word,各種源代碼文本......

工作原理

image

模板:就是一份已經(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ù)如下:


image

這一段代碼在頁面輸出的就是:


image

<#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}

頁面輸出如下:


image
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視圖如下:


image

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ù)如下:


image

在index.ftl中遍歷

<#--list-->
<#list goodsList as fruit>
    索引:${fruit_index},
    水果:${fruit.name};
    價(jià)格:${fruit.price}
</#list>

頁面顯示為:


image
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')}

顯示如下:


image
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í)什么都不顯示{emp.name!(“名字為空”)} 表示 name 為空時(shí)顯示 名字為空
{(emp.company.name)!} 表示如果 company 對(duì)象為空則什么都不顯示憔儿, !只用 在最近的那個(gè)屬性判斷;所以如果遇上有自定義類型(導(dǎo)航)屬性時(shí)放可,需要使用括號(hào){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>

顯示如下:


image
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):


image

創(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 訪問該接口肖爵,得到頁面如下:


image

上面這個(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末含末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子即舌,更是在濱河造成了極大的恐慌佣盒,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顽聂,死亡現(xiàn)場(chǎng)離奇詭異肥惭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)芜飘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門务豺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嗦明,你說我怎么就攤上這事笼沥。” “怎么了娶牌?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵奔浅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我诗良,道長(zhǎng)汹桦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任鉴裹,我火速辦了婚禮舞骆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘径荔。我一直安慰自己督禽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布总处。 她就那樣靜靜地躺著狈惫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鹦马。 梳的紋絲不亂的頭發(fā)上胧谈,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天忆肾,我揣著相機(jī)與錄音,去河邊找鬼菱肖。 笑死客冈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蔑滓。 我是一名探鬼主播郊酒,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼键袱!你這毒婦竟也來了燎窘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤蹄咖,失蹤者是張志新(化名)和其女友劉穎褐健,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澜汤,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚜迅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俊抵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谁不。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖徽诲,靈堂內(nèi)的尸體忽然破棺而出刹帕,到底是詐尸還是另有隱情,我是刑警寧澤谎替,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布偷溺,位于F島的核電站,受9級(jí)特大地震影響钱贯,放射性物質(zhì)發(fā)生泄漏挫掏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一秩命、第九天 我趴在偏房一處隱蔽的房頂上張望尉共。 院中可真熱鬧,春花似錦弃锐、人聲如沸爸邢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碌尔,卻和暖如春浇辜,著一層夾襖步出監(jiān)牢的瞬間券敌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工柳洋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留待诅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓熊镣,卻偏偏與公主長(zhǎng)得像卑雁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绪囱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容