FreeMarker入門級使用及靜態(tài)化

1. FreeMarker 介紹

FreeMarker是一款模板引擎: 即一種基于模板和要改變的數(shù)據(jù)唐片, 并用來生成輸出文本(HTML網(wǎng)頁、電子郵件配置文件源代碼等)的通用工具惕鼓。 它不是面向最終用戶的,而是一個Java類庫唐础,是一款程序員可以嵌入他們所開發(fā)產(chǎn)品的組件。

FreeMarker是免費的矾飞,基于Apache許可證2.0版本發(fā)布一膨。其模板編寫為FreeMarker Template Language(FTL),屬于簡單洒沦、專用的語言豹绪。需要準備數(shù)據(jù)在真實編程語言中來顯示,比如數(shù)據(jù)庫查詢和業(yè)務(wù)運算申眼, 之后模板顯示已經(jīng)準備好的數(shù)據(jù)瞒津。在模板中,主要用于如何展現(xiàn)數(shù)據(jù)括尸, 而在模板之外注意于要展示什么數(shù)據(jù) [1] 巷蚪。

1、模板+數(shù)據(jù)模型=輸出

freemarker并不關(guān)心數(shù)據(jù)的來源濒翻,只是根據(jù)模板的內(nèi)容屁柏,將數(shù)據(jù)模型在模板中顯示并輸出文件(通常為html,也
可以生成其它格式的文本文件)

2. FreeMarker 快速入門

freemarker作為springmvc一種視圖格式有送,默認情況下SpringMVC支持freemarker視圖格式淌喻。

需要創(chuàng)建Spring Boot+Freemarker工程用于測試模板。

1.1 創(chuàng)建測試工程

創(chuàng)建一個freemarker 的測試工程專門用于freemarker的功能測試與模板的測試雀摘。

pom.xml如下

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
    </dependency>
</dependencies>

1.2 配置文件

配置application.yml和 logback-spring.xml裸删,從cms工程拷貝這兩個文件,進行更改阵赠, logback-spring.xml無需更改涯塔,application.yml內(nèi)容如下:

server:
  port: 8088 #服務(wù)端口

spring:
  application:
    name: test-freemarker #指定服務(wù)名
  freemarker:
    cache: false  #關(guān)閉模板緩存肌稻,方便測試
    settings:
      template_update_delay: 0 #檢查模板更新延遲時間,設(shè)置為0表示立即檢查伤塌,如果時間大于0會有緩存不方便進行模板測試

1.3 創(chuàng)建模型類

在freemarker的測試工程下創(chuàng)建模型類型用于測試

package com.xuecheng.test.freemarker.model;
@Data
@ToString
public class Student {
    private String name;//姓名
    private int age;//年齡
    private Date birthday;//生日
    private Float mondy;//錢包
    private List<Student> friends;//朋友列表
    private Student bestFriend;//最好的朋友
}

1.4 創(chuàng)建模板

在 src/main/resources下創(chuàng)建templates灯萍,此目錄為freemarker的默認模板存放目錄。

在templates下創(chuàng)建模板文件test1.ftl每聪,模板中的${name}最終會被freemarker替換成具體的數(shù)據(jù)旦棉。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf‐8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!
</body>
</html>

1.5 創(chuàng)建controller

創(chuàng)建Controller類,向Map中添加name药薯,最后返回模板文件绑洛。

package com.xuecheng.test.freemarker.controller;
@RequestMapping("/freemarker")
@Controller
public class FreemarkerController {
    @Autowired
    RestTemplate restTemplate;
    @RequestMapping("/test1")
    public String freemarker(Map<String, Object> map){
        map.put("name","黑馬程序員");
        //返回模板文件名稱
        return "test1";
    }
}

1.6 創(chuàng)建啟動類

@SpringBootApplication
public class FreemarkerTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(FreemarkerTestApplication.class,args);
    }
}

1.7 測試

請求:http://localhost:8088/freemarker/test1

屏幕顯示: Hello 黑馬程序員!

3. FreeMarker 基礎(chǔ)

3.1 核心指令

3.1.1 數(shù)據(jù)模型

Freemarker靜態(tài)化依賴數(shù)據(jù)模型和模板,下邊定義數(shù)據(jù)模型:

下邊方法形參map即為freemarker靜態(tài)化所需要的數(shù)據(jù)模型童本,在map中填充數(shù)據(jù):

@RequestMapping("/test1")
public String freemarker(Map<String, Object> map){
    //向數(shù)據(jù)模型放數(shù)據(jù)
    map.put("name","黑馬程序員");
    Student stu1 = new Student();
    stu1.setName("小明");
    stu1.setAge(18);
    stu1.setMondy(1000.86f);
    stu1.setBirthday(new Date());
    Student stu2 = new Student();
    stu2.setName("小紅");
    stu2.setMondy(200.1f);
    stu2.setAge(19);
    //        stu2.setBirthday(new Date());
    List<Student> friends = new ArrayList<>();
    friends.add(stu1);
    stu2.setFriends(friends);
    stu2.setBestFriend(stu1);
    List<Student> stus = new ArrayList<>();
    stus.add(stu1);
    stus.add(stu2);
    //向數(shù)據(jù)模型放數(shù)據(jù)
    map.put("stus",stus);
    //準備map數(shù)據(jù)
    HashMap<String,Student> stuMap = new HashMap<>();
    stuMap.put("stu1",stu1);
    stuMap.put("stu2",stu2);
    //向數(shù)據(jù)模型放數(shù)據(jù)
    map.put("stu1",stu1);
    //向數(shù)據(jù)模型放數(shù)據(jù)
    map.put("stuMap",stuMap);
    //返回模板文件名稱
    return "test1";
}

3.1.2 List指令

本節(jié)定義freemarker模板真屯,模板中使用freemarker的指令,關(guān)于freemarker的指令需要知道:

  1. 注釋穷娱,即<#‐‐和‐‐>绑蔫,介于其之間的內(nèi)容會被freemarker忽略
  2. 插值(Interpolation):即${..}部分,freemarker會用真實的值代替${..}
  3. FTL指令:和HTML標記類似,名字前加#予以區(qū)分泵额,F(xiàn)reemarker會解析標簽中的表達式或邏輯配深。
  4. 文本,僅文本信息嫁盲,這些不是freemarker的注釋篓叶、插值、FTL指令的內(nèi)容會被freemarker忽略解析羞秤,直接輸出內(nèi)容缸托。

在test1.ftl模板中使用list指令遍歷數(shù)據(jù)模型中的數(shù)據(jù):

<table>
    <tr>
     <td>序號</td>    
        <td>姓名</td>
        <td>年齡</td>
        <td>錢包</td>
    </tr>
    <#list stus as stu>
        <tr>
            <td>${stu_index + 1}</td>
            <td>${stu.name}</td>
            <td>${stu.age}</td>
            <td>${stu.mondy}</td>
        </tr>
    </#list>
</table>

輸出:

Hello 黑馬程序員! 序號 姓名 年齡 錢包 1 小明 18 1,000.86 2 小紅 19 200.1

說明:
_index:得到循環(huán)的下標,使用方法是在stu后邊加"_index"瘾蛋,它的值是從0開始

3.1.3 遍歷Map數(shù)據(jù)

  1. 數(shù)據(jù)模型

    使用map指令遍歷數(shù)據(jù)模型中的stuMap俐镐。

  2. 模板

    輸出stu1的學(xué)生信息:<br/>
    姓名:${stuMap['stu1'].name}<br/>
    年齡:${stuMap['stu1'].age}<br/>
    輸出stu1的學(xué)生信息:<br/>
    姓名:${stuMap.stu1.name}<br/>
    年齡:${stuMap.stu1.age}<br/>
    遍歷輸出兩個學(xué)生信息:<br/>
    <table>
        <tr>
            <td>序號</td>
            <td>姓名</td>
            <td>年齡</td>
            <td>錢包</td>
        </tr>
    <#list stuMap?keys as k>
    <tr>
        <td>${k_index + 1}</td>
        <td>${stuMap[k].name}</td>
        <td>${stuMap[k].age}</td>
        <td >${stuMap[k].mondy}</td>
    </tr>
    </#list>
    </table>
    
  3. 輸出

    輸出stu1的學(xué)生信息:
    姓名:小明
    年齡:18
    輸出stu1的學(xué)生信息:
    姓名:小明
    年齡:18
    遍歷輸出兩個學(xué)生信息:
    序號 姓名 年齡 錢包        
    1 小紅 19 200.1         
    2 小明 18 1,000.86
    

3.1.4 if指令

if 指令即判斷指令,是常用的FTL指令哺哼,freemarker在解析時遇到if會進行判斷京革,條件為真則輸出if中間的內(nèi)容,否則跳過內(nèi)容不再輸出幸斥。

  1. 數(shù)據(jù)模型:

    使用list指令中測試數(shù)據(jù)模型匹摇。

  2. 模板:

    <table>
        <tr>
            <td>姓名</td>
            <td>年齡</td>
            <td>錢包</td>
        </tr>
        <#list stus as stu>
            <tr>
                <td <#if stu.name =='小明'>style="background:red;"</#if>>${stu.name}</td>
             <td>${stu.age}</td>
             <td >${stu.mondy}</td>
         </tr>
     </#list>
    </table>
    
  3. 輸出:

    通過測試發(fā)現(xiàn) 姓名為小明的背景色為紅色。

3.2 其它指令

3.2.1 運算符

  1. 算數(shù)運算符 FreeMarker表達式中完全支持算術(shù)運算,FreeMarker支持的算術(shù)運算符包括:+,-,*, /, %
  2. 邏輯運算符 邏輯運算符有如下幾個: 邏輯與:&& 邏輯或:|| 邏輯非:!邏輯運算符只能作用于布爾值,否則將產(chǎn)生錯誤
  3. 比較運算符 表達式中支持的比較運算符有如下幾個:
    1. =或者==:判斷兩個值是否相等.
    2. !=:判斷兩個值是否不等.
    3. >或者gt:判斷左邊值是否大于右邊值
    4. >=或者gte:判斷左邊值是否大于等于右邊值
    5. <或者lt:判斷左邊值是否小于右邊值
    6. <=或者lte:判斷左邊值是否小于等于右邊值

注意: =和!=可以用于字符串,數(shù)值和日期來比較是否相等,但=和!=兩邊必須是相同類型的值,否則會產(chǎn)生錯誤,而且FreeMarker是精確比較,"x","x ","X"是不等的.其它的運行符可以作用于數(shù)字和日期,但不能作用于字符串,大部分的時候,使用gt等字母運算符代替>會有更好的效果,因為 FreeMarker會把>解釋成FTL標簽的結(jié)束字符,當然,也可以使用括號來避免這種情況,如:<#if (x>y)>

3.2.2 空值處理

  1. 判斷某變量是否存在使用 “??” 用法為:variable??,如果該變量存在,返回true,否則返回false

    例:為防止stus為空報錯可以加上判斷如下:

    <#if stus??>
        <#list stus as stu>
            ......    
         </#list>
    </#if>
    
  2. 缺失變量默認值使用 “!” 使用!要以指定一個默認值甲葬,當變量為空時顯示默認值廊勃。

    例: ${name!''}表示如果name為空顯示空字符串。

    如果是嵌套對象則建議使用()括起來。

    例: ${(stu.bestFriend.name)!''}表示坡垫,如果stubestFriendname為空默認顯示空字符串梭灿。

3.2.3 內(nèi)建函數(shù)

內(nèi)建函數(shù)語法格式: 變量+?+函數(shù)名稱

  1. 得到某個集合的大小

    ${集合名?size}

  2. 日期格式化

    • 顯示年月日: ${today?date}
    • 顯示時分秒:${today?time}
    • 顯示日期+時間:?${today?datetime} <br>
    • 自定義格式化: ${today?string("yyyy年MM月")}
  3. 內(nèi)建函數(shù)c

    map.put("point", 102920122);
    point是數(shù)字型,使用${point}會顯示這個數(shù)字的值冰悠,并且每三位使用逗號分隔堡妒。
    如果不想顯示為每三位分隔的數(shù)字,可以使用c函數(shù)將數(shù)字型轉(zhuǎn)成字符串輸出
    ${point?c}

  4. 將json字符串轉(zhuǎn)成對象

    一個例子:
    其中用到了 assign標簽溉卓,assign的作用是定義一個變量皮迟。

    <#assign text="{'bank':'工商銀行','account':'10101920201920212'}" />
    <#assign data=text?eval />
    開戶行:${data.bank}  賬號:${data.account}
    

3.2.4 完整的模板

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf‐8">
        <title>Hello World!</title>
    </head>
    <body>
        Hello ${name}!
        <br/>
        <table>
            <tr>
                <td>序號</td>
                <td>姓名</td>
                <td>年齡</td>
                <td>錢包</td>
            </tr>
            <#list stus as stu>
                <tr>
                    <td>${stu_index + 1}</td>
                    <td <#if stu.name =='小明'>style="background:red;"</#if>>${stu.name}</td>
        <td>${stu.age}</td>
        <td >${stu.mondy}</td>
        </tr>
    </#list>
</table>
<br/><br/>
輸出stu1的學(xué)生信息:<br/>
姓名:${stuMap['stu1'].name}<br/>
年齡:${stuMap['stu1'].age}<br/>
輸出stu1的學(xué)生信息:<br/>
姓名:${stu1.name}<br/>
年齡:${stu1.age}<br/>
遍歷輸出兩個學(xué)生信息:<br/>
<table>
    <tr>
        <td>序號</td>
        <td>姓名</td>
        <td>年齡</td>
        <td>錢包</td>
    </tr>
    <#list stuMap?keys as k>
        <tr>
            <td>${k_index + 1}</td>
            <td>${stuMap[k].name}</td>
            <td>${stuMap[k].age}</td>
            <td >${stuMap[k].mondy}</td>
        </tr>
        </#list>
</table>
</br>
<table>
    <tr>
        <td>姓名</td>
        <td>年齡</td>
        <td>出生日期</td>
        <td>錢包</td>
        <td>最好的朋友</td>
        <td>朋友個數(shù)</td>
        <td>朋友列表</td>
    </tr>
    <#if stus??>
        <#list stus as stu>
            <tr>
                <td>${stu.name!''}</td>
                <td>${stu.age}</td>
                <td>${(stu.birthday?date)!''}</td>
                <td>${stu.mondy}</td>
                <td>${(stu.bestFriend.name)!''}</td>
                <td>${(stu.friends?size)!0}</td>
                <td>
                    <#if stu.friends??>
                        <#list stu.friends as firend>
                            ${firend.name!''}<br/>
                         </#list>
                    </#if>
                </td>
            </tr>
            </#list>
        </#if>
</table>
<br/>
<#assign text="{'bank':'工商銀行','account':'10101920201920212'}" />
<#assign data=text?eval />
開戶行:${data.bank}  賬號:${data.account}
</body>
</html>

3.3 靜態(tài)化測試

3.3.1 使用模板文件靜態(tài)化

在test下創(chuàng)建測試類,并且將main下的resource/templates拷貝到test下桑寨,本次測試使用之前我們在main下創(chuàng)建的模板文件伏尼。

//基于模板生成靜態(tài)化文件
@Test
public void testGenerateHtml() throws IOException, TemplateException {
    //創(chuàng)建配置類
    Configuration configuration=new Configuration(Configuration.getVersion());
    //設(shè)置模板路徑
    String classpath = this.getClass().getResource("/").getPath();
    configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/"));
    //設(shè)置字符集
    configuration.setDefaultEncoding("utf‐8");
    //加載模板
    Template template = configuration.getTemplate("test1.ftl");
    //數(shù)據(jù)模型
    Map<String,Object> map = new HashMap<>();
    map.put("name","黑馬程序員");
    //靜態(tài)化
    String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
    //靜態(tài)化內(nèi)容
    System.out.println(content);
    InputStream inputStream = IOUtils.toInputStream(content);
    //輸出文件
    FileOutputStream fileOutputStream = new FileOutputStream(new File("d:/test1.html"));
    int copy = IOUtils.copy(inputStream, fileOutputStream);
}

3.3.2 使用模板字符串靜態(tài)化

//基于模板字符串生成靜態(tài)化文件
@Test
public void testGenerateHtmlByString() throws IOException, TemplateException {
    //創(chuàng)建配置類
    Configuration configuration=new Configuration(Configuration.getVersion());
    //模板內(nèi)容,這里測試時使用簡單的字符串作為模板
    String templateString="" +
        "<html>\n" +
        "    <head></head>\n" +
        "    <body>\n" +
        "    名稱:${name}\n" +
        "    </body>\n" +
        "</html>";

    //模板加載器
    StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
    stringTemplateLoader.putTemplate("template",templateString);
    configuration.setTemplateLoader(stringTemplateLoader);
    //得到模板
    Template template = configuration.getTemplate("template","utf‐8");
    //數(shù)據(jù)模型
    Map<String,Object> map = new HashMap<>();
    map.put("name","黑馬程序員");
    //靜態(tài)化
    String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
    //靜態(tài)化內(nèi)容
    System.out.println(content);
    InputStream inputStream = IOUtils.toInputStream(content);
    //輸出文件
    FileOutputStream fileOutputStream = new FileOutputStream(new File("d:/test1.html"));
    IOUtils.copy(inputStream, fileOutputStream);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尉尾,一起剝皮案震驚了整個濱河市爆阶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沙咏,老刑警劉巖辨图,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異肢藐,居然都是意外死亡徒役,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門窖壕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杉女,你說我怎么就攤上這事瞻讽。” “怎么了熏挎?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵速勇,是天一觀的道長。 經(jīng)常有香客問我坎拐,道長烦磁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任哼勇,我火速辦了婚禮都伪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘积担。我一直安慰自己陨晶,他們只是感情好,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布帝璧。 她就那樣靜靜地躺著先誉,像睡著了一般湿刽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上褐耳,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天诈闺,我揣著相機與錄音,去河邊找鬼铃芦。 笑死雅镊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的杨帽。 我是一名探鬼主播漓穿,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼注盈!你這毒婦竟也來了晃危?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤老客,失蹤者是張志新(化名)和其女友劉穎僚饭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胧砰,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡鳍鸵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尉间。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偿乖。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖哲嘲,靈堂內(nèi)的尸體忽然破棺而出贪薪,到底是詐尸還是另有隱情,我是刑警寧澤眠副,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布画切,位于F島的核電站,受9級特大地震影響囱怕,放射性物質(zhì)發(fā)生泄漏霍弹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一娃弓、第九天 我趴在偏房一處隱蔽的房頂上張望典格。 院中可真熱鬧,春花似錦台丛、人聲如沸钝计。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽私恬。三九已至债沮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間本鸣,已是汗流浹背疫衩。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留荣德,地道東北人闷煤。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像涮瞻,于是被迫代替她去往敵國和親鲤拿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

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