Freemarker研究

1.1 FreeMarker介紹

1主经、 freemarker是一個(gè)用Java開(kāi)發(fā)的模板引擎

FreeMarker是一款模板引擎: 即一種基于模板和要改變的數(shù)據(jù)键痛, 并用來(lái)生成輸出文本(HTML網(wǎng)頁(yè)挽牢、電子郵件配置文件第股、源代碼等)的通用工具。 它不是面向最終用戶的克握,而是一個(gè)Java類(lèi)庫(kù),是一款程序員可以嵌入他們所開(kāi)發(fā)產(chǎn)品的組件枷踏。
FreeMarker是免費(fèi)的菩暗,基于Apache許可證2.0版本發(fā)布。其模板編寫(xiě)為FreeMarker Template Language(FTL)旭蠕,屬于簡(jiǎn)單停团、專(zhuān)用的語(yǔ)言。需要準(zhǔn)備數(shù)據(jù)在真實(shí)編程語(yǔ)言中來(lái)顯示掏熬,比如數(shù)據(jù)庫(kù)查詢和業(yè)務(wù)運(yùn)算佑稠, 之后模板顯示已經(jīng)準(zhǔn)備好的數(shù)據(jù)。在模板中旗芬,主要用于如何展現(xiàn)數(shù)據(jù)舌胶, 而在模板之外注意于要展示什么數(shù)據(jù) [1]

image.png

常用的java模板引擎還有哪些疮丛?

Jsp幔嫂、Freemarker、Thymeleaf 誊薄、Velocity 等

2履恩、模板+數(shù)據(jù)模型=輸出

freemarker并不關(guān)心數(shù)據(jù)的來(lái)源,只是根據(jù)模板的內(nèi)容呢蔫,將數(shù)據(jù)模型在模板中顯示并輸出文件(通常為html切心,也可以生成其它格式的文本文件)
1、數(shù)據(jù)模型
數(shù)據(jù)模型在java中可以是基本類(lèi)型也可以List片吊、Map绽昏、Pojo等復(fù)雜類(lèi)型。
2俏脊、來(lái)自官方的例子:(https://freemarker.apache.org/docs/dgui_quickstart_basics.html

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


模板:
image.png

輸出:
image.png

1.2 FreeMarker快速入門(mén)

freemarker作為springmvc一種視圖格式而涉,默認(rèn)情況下SpringMVC支持freemarker視圖格式。
需要?jiǎng)?chuàng)建Spring Boot+Freemarker工程用于測(cè)試模板联予。

1.2.1 創(chuàng)建測(cè)試工程

創(chuàng)建一個(gè)freemarker 的測(cè)試工程專(zhuān)門(mén)用于freemarker的功能測(cè)試與模板的測(cè)試。
pom如下:

    <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.2 配置文件

yaml

server:
  port: 8088

spring:
  application:
    name: test‐freemarker #指定服務(wù)名
  freemarker:
    cache: false #關(guān)閉模板緩存材原,方便測(cè)試
    settings:
      template_update_delay: 0 #檢查模板更新延遲時(shí)間沸久,設(shè)置為0表示立即檢查,如果時(shí)間大于0會(huì)有緩存不方便進(jìn)行模板測(cè)試
1.2.3 創(chuàng)建模型類(lèi)

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

@Data
@ToString
public class Student {
    private String name;//姓名
    private int age;//年齡
    private Date birthday;//生日
    private Float money;//錢(qián)包
    private List<Student> friends;//朋友列表
    private Student bestFriend;//最好的朋友
}

1.2.4 創(chuàng)建模板

在 src/main/resources下創(chuàng)建templates余蟹,此目錄為freemarker的默認(rèn)模板存放目錄卷胯。在templates下創(chuàng)建模板文件test1.ftl,模板中的${name}最終會(huì)被freemarker替換成具體的

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf‐8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!
</body>
</html>
1.2.5 創(chuàng)建controller

創(chuàng)建Controller類(lèi)威酒,向Map中添加name窑睁,最后返回模板文件

@RequestMapping("/freemarker")
@Controller
public class FreemarkerController {

    @RequestMapping("/test1")
    public String freemarker(Map<String, Object> map){
        map.put("name","小小程序員");
        //返回模板文件名稱挺峡,默認(rèn)是templates包下的
        return "test1";
    }

}
1.2.6 測(cè)試

請(qǐng)求:http://localhost:8088/freemarker/test1
屏幕顯示:Hello 小小程序員!

1.3 FreeMarker基礎(chǔ)

1.3.1 核心基礎(chǔ)
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.setMoney(1000.86f);
        stu1.setBirthday(new Date());
        Student stu2 = new Student();
        stu2.setName("小紅");
        stu2.setMoney(200.1f);
        stu2.setAge(19);
//        stu2.setBirthday(new Date());
        List<Student> friends = new ArrayList<>();
        friends.add(stu1);
        //給第二名學(xué)生好朋友列表
        stu2.setFriends(friends);
        //給第二名學(xué)生最好的朋友
        stu2.setBestFriend(stu1);
        List<Student> stus = new ArrayList<>();
        stus.add(stu1);
        stus.add(stu2);
        //向數(shù)據(jù)模型放數(shù)據(jù)
        map.put("stus",stus);
        //準(zhǔn)備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";
    }
1.3.1.2 List指令

1橱赠、注釋?zhuān)?lt;#‐‐和‐‐>,介于其之間的內(nèi)容會(huì)被freemarker忽略
2箫津、插值(Interpolation):即${..}部分,freemarker會(huì)用真實(shí)的值代替${..}
3狭姨、FTL指令:和HTML標(biāo)記類(lèi)似,名字前加#予以區(qū)分苏遥,F(xiàn)reemarker會(huì)解析標(biāo)簽中的表達(dá)式或邏輯饼拍。
4、文本田炭,僅文本信息师抄,這些不是freemarker的注釋、插值教硫、FTL指令的內(nèi)容會(huì)被freemarker忽略解析叨吮,直接輸出內(nèi)容。

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

<table>
    <tr>
        <td>序號(hào)</td>
        <td>姓名</td>
        <td>年齡</td>
        <td>錢(qián)包</td>
    </tr>
    <#list stus as stu>
        <tr>
            <td>${stu_index+1}</td>
            <td>${stu.name}</td>
            <td>${stu.age}</td>
            <td>${stu.money}</td>
        </tr>
    </#list>
</table>
1.3.1.3 遍歷Map數(shù)據(jù)

1栋豫、數(shù)據(jù)模型
使用map指令遍歷數(shù)據(jù)模型中的stuMap挤安。
2、模板

   <#--遍歷map-->
    姓名:${stuMap['stu1'].name}<br/>
    年齡:${stuMap['stu1'].age}<br/>

    姓名:${stuMap.stu1.name}<br/>
    年齡:${stuMap.stu1.age}<br/>
<table>
    <tr>
        <td>序號(hào)</td>
        <td>姓名</td>
        <td>年齡</td>
        <td>錢(qián)包</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>
1.3.1.4 if指令

if 指令即判斷指令丧鸯,是常用的FTL指令蛤铜,freemarker在解析時(shí)遇到if會(huì)進(jìn)行判斷,條件為真則輸出if中間的內(nèi)容丛肢,否
則跳過(guò)內(nèi)容不再輸出围肥。
1、數(shù)據(jù)模型:
使用list指令中測(cè)試數(shù)據(jù)模型蜂怎。
2穆刻、模板:

<table>
    <tr>
        <td>序號(hào)</td>
        <td>姓名</td>
        <td>年齡</td>
        <td>錢(qián)包</td>
    </tr>
    <#--list指令-->
    <#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 <#if (stu.money > 200)>style="background:red;"</#if> >${stu.money}</td>
        </tr>
    </#list>
</table>

3、輸出:
通過(guò)測(cè)試發(fā)現(xiàn) 姓名為小明的背景色為紅色杠步。

1.3.2 其它指令

1.3.2.1 運(yùn)算符

1氢伟、算數(shù)運(yùn)算符 FreeMarker表達(dá)式中完全支持算術(shù)運(yùn)算,FreeMarker支持的算術(shù)運(yùn)算符包括:+, - , * , / , %

2、邏輯運(yùn)算符 邏輯運(yùn)算符有如下幾個(gè): 邏輯與:&& 邏輯或:|| 邏輯非:! 邏輯運(yùn)算符只能作用于布爾值,否則將產(chǎn)生錯(cuò)誤

3幽歼、比較運(yùn)算符 表達(dá)式中支持的比較運(yùn)算符有如下幾個(gè):1 =或者==:判斷兩個(gè)值是否相等. 2 !=:判斷兩個(gè)值是否不等. 3 >或者gt:判斷左邊值是否大于右邊值

4 >=或者gte:判斷左邊值是否大于等于右邊值

5 <或者lt:判斷左邊值是否小于右邊值

6 <=或者lte:判斷左邊值是否小于等于右邊值

注意: =和!=可以用于字符串,數(shù)值和日期來(lái)比較是否相等,但=和!=兩邊必須是相同類(lèi)型的值,否則會(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)來(lái)避免這種情況,如:<#if (x>y)

1.3.2.2 空值處理

1朵锣、判斷某變量是否存在使用 “??” 用法為:variable??,如果該變量存在,返回true,否則返回false
例:為防止stus為空?qǐng)?bào)錯(cuò)可以加上判斷如下:

<#if stus??>
<#list stus as stu>
......
</#list>
</#if>

2、缺失變量默認(rèn)值使用 “!” 使用!要以指定一個(gè)默認(rèn)值甸私,當(dāng)變量為空時(shí)顯示默認(rèn)值诚些。
例: ${name!''}表示如果name為空顯示空字符串。

//默認(rèn)為空
<td>${(stuMap[k].mondy)!""}</td>
1.3.2.3 內(nèi)建函數(shù)

內(nèi)建函數(shù)語(yǔ)法格式: 變量+?+函數(shù)名稱
1皇型、和到某個(gè)集合的大小
${集合名?size}

2诬烹、日期格式化

顯示年月日: ${today?date}
顯示時(shí)分秒:${today?time}
顯示日期+時(shí)間:${today?datetime} <br>
自定義格式化: ${today?string("yyyy年MM月")}

3砸烦、內(nèi)建函數(shù)c
map.put("point", 102920122);
point是數(shù)字型,使用{point}會(huì)顯示這個(gè)數(shù)字的值绞吁,不并每三位使用逗號(hào)分隔幢痘。 如果不想顯示為每三位分隔的數(shù)字,可以使用c函數(shù)將數(shù)字型轉(zhuǎn)成字符串輸出{point?c}

4掀泳、將json字符串轉(zhuǎn)成對(duì)象
一個(gè)例子:
其中用到了 assign標(biāo)簽雪隧,assign的作用是定義一個(gè)變量。

<#assign text="{'bank':'工商銀行','account':'10101920201920212'}" />
<#assign data=text?eval />
開(kāi)戶行:${data.bank} 賬號(hào):${data.account}

1.3.3 靜態(tài)化測(cè)試

在cms中使用freemarker將頁(yè)面生成html文件员舵,本節(jié)測(cè)試html文件生成的方法:

1脑沿、使用模板文件靜態(tài)化
定義模板文件,使用freemarker靜態(tài)化程序生成html文件马僻。

2庄拇、使用模板字符串靜態(tài)化
定義模板字符串,使用freemarker靜態(tài)化程序生成html文件韭邓。

package com.xuecheng.test.freemarker;

import com.xuecheng.test.freemarker.model.Student;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * Description:
 *
 * @author smile
 * @date 2018-12-24
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestFreemarker {


@Test
public void testGenerateHtml() throws IOException, TemplateException {
    //創(chuàng)建配置類(lèi)
    Configuration configuration = new Configuration(Configuration.getVersion());
    String classpath = this.getClass().getResource("/").getPath();
    //設(shè)置模板路徑
    configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/"));
    //設(shè)置字符集
    configuration.setDefaultEncoding("utf-8");
    //加載模板
    Template template = configuration.getTemplate("test1.ftl");
    //數(shù)據(jù)模型
    Map map = getMap();
    //靜態(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);


}


    //基于模板字符串生成靜態(tài)化文件
    @Test
    public void testGenerateHtmlByString() throws IOException, TemplateException {
        //創(chuàng)建配置類(lèi)
        Configuration configuration=new Configuration(Configuration.getVersion());
        //獲取模板內(nèi)容
        //模板內(nèi)容措近,這里測(cè)試時(shí)使用簡(jiǎn)單的字符串作為模板
        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 map = getMap();
        //靜態(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);
    }




    //數(shù)據(jù)模型
    private Map getMap(){
        Map<String, Object> map = new HashMap<>();
        //向數(shù)據(jù)模型放數(shù)據(jù)
        map.put("name","小小程序員");
        Student stu1 = new Student();
        stu1.setName("小明");
        stu1.setAge(18);
        stu1.setMoney(1000.86f);
        stu1.setBirthday(new Date());
        Student stu2 = new Student();
        stu2.setName("小紅");
        stu2.setMoney(200.1f);
        stu2.setAge(19);
        stu2.setBirthday(new Date());
        List<Student> friends = new ArrayList<>();
        friends.add(stu1);
        //給第二名學(xué)生好朋友列表
        stu2.setFriends(friends);
        //給第二名學(xué)生最好的朋友
        stu2.setBestFriend(stu1);
        List<Student> stus = new ArrayList<>();
        stus.add(stu1);
        stus.add(stu2);
        //向數(shù)據(jù)模型放數(shù)據(jù)
        map.put("stus",stus);
        //準(zhǔn)備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 map;
    }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市女淑,隨后出現(xiàn)的幾起案子瞭郑,更是在濱河造成了極大的恐慌,老刑警劉巖鸭你,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屈张,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡袱巨,警方通過(guò)查閱死者的電腦和手機(jī)阁谆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)愉老,“玉大人场绿,你說(shuō)我怎么就攤上這事〖等耄” “怎么了焰盗?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)咒林。 經(jīng)常有香客問(wèn)我姨谷,道長(zhǎng),這世上最難降的妖魔是什么映九? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮瞎颗,結(jié)果婚禮上件甥,老公的妹妹穿的比我還像新娘捌议。我一直安慰自己,他們只是感情好引有,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布瓣颅。 她就那樣靜靜地躺著,像睡著了一般譬正。 火紅的嫁衣襯著肌膚如雪宫补。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,196評(píng)論 1 308
  • 那天曾我,我揣著相機(jī)與錄音粉怕,去河邊找鬼。 笑死抒巢,一個(gè)胖子當(dāng)著我的面吹牛贫贝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛉谜,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼稚晚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了型诚?” 一聲冷哼從身側(cè)響起客燕,我...
    開(kāi)封第一講書(shū)人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狰贯,沒(méi)想到半個(gè)月后也搓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暮现,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年还绘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栖袋。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拍顷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出塘幅,到底是詐尸還是另有隱情昔案,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布电媳,位于F島的核電站踏揣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏匾乓。R本人自食惡果不足惜捞稿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧娱局,春花似錦彰亥、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至耻涛,卻和暖如春废酷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抹缕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工澈蟆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人歉嗓。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓丰介,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親鉴分。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哮幢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

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