模板引擎 — FreeMarker(簡介、快速入門、核心指令、靜態化_基於模板文件/基於模板字符串)


歡迎訪問筆者個人技術博客:http://rukihuang.xyz/

1 FreeMarker介紹

  • FreeMarker是一款模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁、電子郵件、配置文件、源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。
  • FreeMarker是免費的,基於Apache許可證2.0版本發佈。其模板編寫爲FreeMarker Template Language(FTL),屬於簡單、專用的語言。需要準備數據在真實編程語言中來顯示,比如數據庫查詢和業務運算, 之後模板顯示已經準備好的數據。在模板中,主要用於如何展現數據, 而在模板之外注意於要展示什麼數據

在這裏插入圖片描述

1.1 常見的模板引擎

  • JSP
  • Thymeleaf
  • Velocity

1.2 模板+數據模型=輸出

  • freemarker並不關心數據的來源,只是根據模板的內容,將數據模型在模板中顯示並輸出文件(通常爲html,也 可以生成其它格式的文本文件)

1.2.1 數據模型

  • 數據模型在java中可以是基本類型也可以List、Map、Pojo等複雜類型。

在這裏插入圖片描述

1.2.2 模板

在這裏插入圖片描述

1.2.3 輸出

在這裏插入圖片描述

2 FreeMarker快速入門

  • freemarker作爲springmvc一種視圖格式,默認情況下SpringMVC支持freemarker視圖格式。

2.1 pom.xml

  • spring-boot-starter-freemarker
  • spring-boot-starter-web
  • lombok
  • okhttp
  • commons-io
  • spring-boot-starter-test
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xc-framework-parent</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../xc-framework-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>test-freemarker</artifactId>
    <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.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

</project>

2.2 配置文件application.yml

server:
  port: 8088 #服務端口
spring:
  application:
    name: test-freemarker #指定服務名
  freemarker:
    cache: false #關閉模板緩存,方便測試
    settings:
      template_update_delay: 0 #檢查模板更新延遲時間,設置爲0表示立即檢查,如果時間大於0會有緩存不方便進行模板測試

2.3 創建模型類Student

@Data
@ToString
public class Student {
    private String name;
    private int age;
    private Date birthday;
    private Float money;
    private List<Student> friends;
    private Student bestFriend;
}

2.4 創建模板src/resources/templates/test1.ftl

  • src/main/resources下創建templates,此目錄爲freemarker的默認模板存放目錄。

  • templates下創建模板文件test1.ftl,模板中的${name}最終會被freemarker替換成具體的數據。

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

2.5 創建controller

    @RequestMapping("/test1")
    public String test1(Map<String, Object> map) {
    
        //map形參存在request域中,是freemarker模板使用的數據
        map.put("name", "張三");

        //返回freemarker模板的位置,基於resource/templates路徑
        return "test1";
    }

2.6 創建啓動類

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

3 FreeMarker基礎

3.1 核心指令

3.1.1 數據模型

  • Freemarker靜態化依賴數據模型和模板,下邊定義數據模型。下邊方法形參map即爲freemarker靜態化所需要的數據模型,在map中填充數據:
    @RequestMapping("/test1")
    public String test1(Map<String, Object> map) {
        //String
        //map形參存在request域中,是freemarker模板使用的數據
        map.put("name", "張三");

        Student stu1 = new Student();
        stu1.setAge(18);
        stu1.setBirthday(new Date());
        stu1.setMoney(200F);
        stu1.setName("小紅");

        Student stu2 = new Student();
        stu2.setAge(19);
        stu2.setBirthday(new Date());
        stu2.setMoney(100F);
        stu2.setName("小方");

        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);
        
        //list
        //將學生列表放在數據模型中
        map.put("stus", stus);

        Map<String, Student> stuMap = new HashMap<>();
        stuMap.put("stu1", stu1);
        stuMap.put("stu2", stu2);

        //向數據模型放數據
        //model
        map.put("stu1", stu1);
        //map
        map.put("stuMap", stuMap);

        //存放數字
        //number
        map.put("point", 123456789);

        //返回freemarker模板的位置,基於resource/templates路徑
        return "test1";
    }

3.1.2 基礎指令

3.1.2.1 註釋

<#--被註釋的內容-->

3.1.2.2 插值

${name}

3.1.2.3 FTL指令

<#if></#if>

3.1.3 List指令

遍歷數據模型中的list學生信息,(stus)
<table>
    <tr>
        <td>序號</td>
        <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.money}</td>
            <td>${stu.age}</td>
            <td>${stu.birthday?date}</td>
        </tr>
    </#list>
</table>
  • 注意:[item]_index得到循環的下標,值從0開始

3.1.4 遍歷Map數據

<!-- 第一種方法:在中括號中填寫map的key -->    
姓名:${stuMap['stu1'].name}<br>
年齡:${stuMap['stu1'].age}<br>
<!-- 第二種方法:在map後直接“點key” --> 
姓名:${stuMap.stu2.name}<br>
年齡:${stuMap.stu2.age}<br>

<table>
    <tr>
        <td>序號</td>
        <td>姓名</td>
        <td>年齡</td>
        <td>金額</td>
    </tr>
    <!-- 遍歷map中的key, stuMap?keys是key列表(是一個list) -->
    <#list stuMap?keys as k>
        <tr>
            <td>${k_index+1}</td>
            <td>${stuMap[k].name}</td>
            <td>${stuMap[k].age}</td>
            <td>${stuMap[k].money}</td>
    	</tr>
	</#list>
</table>

3.1.5 if指令

  • if 指令即判斷指令,是常用的FTL指令,freemarker在解析時遇到if會進行判斷,條件爲真則輸出if中間的內容,否 則跳過內容不再輸出。
<!-- 如果stus存在-->
<#if stus?? >
    <#list stus as stu>
        <tr>
            <td>${stu_index+1}</td>
            <!-- 如果name爲小方,背景色爲藍色-->
            <td <#if stu.name == "小方"> style="background: cadetblue"</#if>>${stu.name}</td>
<td>${stu.money}</td>
			<!-- 大於號> 會和標籤的尖括號混淆,可以用括號(stu.money > 300) 括起來,或者用gt代替>-->
			<!-- 如果money大於150,背景色爲藍色-->
    		<td <#if stu.money gt 150> style="background: cadetblue"</#if>>${stu.age}</td>
			<td>${stu.birthday?date}</td>
		</tr>
    </#list>
</#if>

3.2 其他指令

3.2.1 運算符

3.2.1.1 算數運算符

  1. /
  2. %

3.2.1.2 邏輯運算符

  1. 與:&&
  2. 或:||
  3. 非:!

3.1.2.3 比較運算符

  1. =
  2. ==
  3. !=
  4. >gt
  5. >=gte
  6. <lt
  7. <=lte

注意:

  1. =!=可以用於字符串,數值和日期來比較是否相等,但=!=兩邊必須是相同類型的值,否則會產生錯誤
  2. 使用gt等字母運算符代替>會有更好的效果,因爲 FreeMarker會把>解釋成FTL標籤的結束字符,當然,也可以使用括 號來避免這種情況
<#if (x>y)>

3.2.2 空值處理

  • 判斷某變量是否存在使用 ??。用法爲:variable??,如果該變量存在,返回true,否則返回false
<#if stus?? >
	...
</#if>
  • 缺失變量默認值使用 ! 使用!要以指定一個默認值,當變量爲空時顯示默認值。
<!-- 如果name爲空顯示空字符串-->
${name!''} 

<!-- 如果是嵌套對象則用()括起來-->
${(stuMap['stu1'].name)!''}

3.2.3 內建函數

  • 語法:變量?函數名稱

3.2.3.1 集合的大小

${集合名?size}

3.2.3.2 日期格式化

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

3.2.3.3 c (數字不用逗號分隔)

<!--c函數將數字型轉爲字符串,123,456,789 -> 123456789-->
${point} 123,456,789
${point?c} 123456789

3.2.4 json轉爲對象 assign eval

  • assign定義變量
  • eval轉化爲對象
<#--json轉對象-->
<#assign text="{'name':'json_name','age':'20'}" />
<#assign data=text?eval />
name:${data.name} <br>
age:${data.age} <br>

4 靜態化

4.1 使用模板文件靜態化

	//基於ftl模板生成html文件
    @Test
    public void testGenerateHtml() throws IOException, TemplateException {
        //1. 定義配置類
        Configuration configuration = new Configuration(Configuration.getVersion());
        //2. 定義模板
        //2.1 得到classpath的路徑
        String classpath = this.getClass().getResource("/").getPath();
        //2.2 定義模板路徑
        configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/"));
        //2.3 獲取模板文件的內容
        Template template = configuration.getTemplate("test1.ftl");
        //3. 定義數據模型
        Map map = getMap();

        //4. 靜態化
        String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
//        System.out.println(content);

        InputStream inputStream = IOUtils.toInputStream(content);
        FileOutputStream outputStream = new FileOutputStream(new File("d:/prac_project/test1.html"));

        //輸出文件
        IOUtils.copy(inputStream, outputStream);
        inputStream.close();
        outputStream.close();
    }

4.2 使用模板字符串靜態化(建議)

//根據模板內容(字符串)生成html
    @Test
    public void testGenerateHtmlByString() throws IOException, TemplateException {
        //1. 定義配置類
        Configuration configuration = new Configuration(Configuration.getVersion());
        //2. 定義模板
        //2.1 模板內容(字符串)
        String templateString = "" +
                "<html>\n" +
                "    <head></head>\n" +
                "    <body>\n" +
                "    名稱:${name}\n" +
                "    </body>\n" +
                "</html>";
        //2.2 使用模板加載器將字符串變爲模板
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("template", templateString);
        //2.3 在配置中設置模板加載器
        configuration.setTemplateLoader(stringTemplateLoader);
        //2.4 獲得模板
        Template template = configuration.getTemplate("template", "utf-8");

        //3. 定義數據模型
        Map map = getMap();

        //4. 靜態化
        String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
//        System.out.println(content);

        InputStream inputStream = IOUtils.toInputStream(content);
        FileOutputStream outputStream = new FileOutputStream(new File("d:/prac_project/test1.html"));

        //輸出文件
        IOUtils.copy(inputStream, outputStream);
        inputStream.close();
        outputStream.close();
    }
    //獲取數據模型
    public Map getMap() {
        Map map = new HashMap();
        //map形參存在request域中,是freemarker模板使用的數據
        map.put("name", "張三");

        Student stu1 = new Student();
        stu1.setAge(18);
        stu1.setBirthday(new Date());
        stu1.setMoney(200F);
        stu1.setName("小紅");

        Student stu2 = new Student();
        stu2.setAge(19);
        stu2.setBirthday(new Date());
        stu2.setMoney(100F);
        stu2.setName("小方");

        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);
        //將學生列表放在數據模型中
        map.put("stus", stus);

        Map<String, Student> stuMap = new HashMap<>();
        stuMap.put("stu1", stu1);
        stuMap.put("stu2", stu2);

        //向數據模型放數據
        map.put("stu1", stu1);
        map.put("stuMap", stuMap);

        //存放數字
        map.put("point", 123456789);

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