0 freemarker簡介
首先我們要先通過 官網:https://freemarker.apache.org/ 了解什麼是freemarker 以及他的使用方法。官方介紹如下:
Apache FreeMarker™是一個模闆引擎:一個Java庫,用于根據模闆和更改資料生成文本輸出(HTML網頁,電子郵件,配置檔案,源代碼等)。模闆是用FreeMarker模闆語言(FTL)編寫的,這是一種簡單的專用語言(不像PHP這樣的完整程式設計語言)。通常,使用通用程式設計語言(如Java)來準備資料(釋出資料庫查詢,進行業務計算)。然後,Apache FreeMarker使用模闆顯示準備好的資料。在模闆中,您将關注如何呈現資料,而在模闆之外,您将關注于要呈現的資料。

這種方法通常被稱為MVC(模型視圖控制器)模式,并且特别受動态網頁的歡迎。它有助于将網頁設計者(HTML作者)與開發人員(通常是Java程式員)分開。設計人員不會在模闆中面對複雜的邏輯,并且可以在程式員不必更改或重新編譯代碼的情況下更改頁面的外觀。
雖然FreeMarker最初是為在MVC Web應用程式架構中生成HTML頁面而建立的,但它并沒有綁定到servlet或HTML或任何與Web相關的内容。它也用于非Web應用程式環境。
通過點選下圖紅色框文檔手冊 閱讀如何使用 freemarker
接下來開始介紹freemarker 基本用法。介紹之前先說明一下我們的開發環境
0.1 版本說明
IDEA :STS
JDK:1.8
freemarker Version:2.3.28
這裡将通過maven項目進行說明我們的用法。首先引入freemarker 依賴到我們的maven項目中
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
1 freemarker 用法之 Hello word
在resource目錄下建立template目錄并添加helloworld.ftl 模闆檔案 内容如下:
<html>
<head>
<title>hello world</title>
</head>
<body>
<h1>this is ${who} hello World</h1>
</body>
</html>
建立配置執行個體 并将模闆和資料進行輸出
package cn.lijunkui.examples;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
public class FreemarkerDemo {
@Test
public void helloWord() throws IOException, TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
//指定模闆檔案的來源
String path = FreemarkerDemo.class.getClassLoader().getResource("template").getPath();
cfg.setDirectoryForTemplateLoading(new File(path));
//這是模闆的編碼
cfg.setDefaultEncoding("UTF-8");
//擷取模闆
Template template = cfg.getTemplate("helloworld.ftl");
//建立FreeMarker的資料模型
Map<String,String> root = new HashMap<String,String>();
root.put("who","freemarker");
//這是輸出檔案
File file = new File("D://" +"helloWord.html");
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
//将模闆與資料模型合并
template.process(root, out);
out.flush();
out.close();
}
}
2 data-model:資料模型
hello word的示例其實就是 Template + data-model = output 接下來我們引用一下官方的示例介紹一下資料模型
資料模型是樹狀結構 如下圖
以上隻是一個可視化; 資料模型不是文本格式,而是來自Java對象。對于Java程式員來說,root可能是帶有
getUser()
和
getLatestProduct()
方法的Java對象,或者是
Map
帶有
"user"
和
"latestProducts"
鍵的Java 。同樣,
latestProduct
也許是一個Java對象
getUrl()
和
getName()
方法。
<html>
<head>
<title>歡迎!</ title>
</head>
<body>
<h1>歡迎${user}!</ h1>
<p>我們的最新産品:
<a href ="${latestProduct.url}"> ${latestProduct.name} </a>!
</body>
</html>
@Test
public void dataModel() throws IOException, TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
//指定模闆檔案的來源
String path = FreemarkerDemo.class.getClassLoader().getResource("template").getPath();
cfg.setDirectoryForTemplateLoading(new File(path));
//這是模闆的編碼
cfg.setDefaultEncoding("UTF-8");
//擷取模闆
Template template = cfg.getTemplate("data-model.ftl");
//建立FreeMarker的資料模型
Map<String,Object> root = new HashMap<String,Object>();
root.put("user","Big Joe");
Product product = new Product();
product.setName("綠色滑鼠");
product.setUrl("products/greenmouse.html");
root.put("latestProduct",product);
//這是輸出檔案
File file = new File("D://" +"dataModel.html");
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
//将模闆與資料模型合并
template.process(root, out);
out.flush();
out.close();
}
3 freemarker 注釋
我們可以通過 <#-- 注釋内容 --> 來進行注釋 如下圖
4 freemarker 條件指令
我們可以通過<#if> <#elseIf> <#else> 進行條件判斷邏輯處理 具體操作如下
條件指令模闆内容:
<html>
<head>
<title>條件指令介紹!</ title>
</head>
<body>
<#-- 條件指令介紹 -->
<#if (5 > 4) >
5 比較大
</#if>
-------------------------------
<#if (!false)>
!false == true
</#if>
-----------------------------
<#if ("a" == "b")>
a 和 b 相同
<#else>
a 和 b 不相同
</#if>
-----------------------------
<#if ( c == d)>
c 和 d 相同
<#elseif ("a" != "b")>
c 和 d 不相同
<#else>
出錯了!
</#if>
</body>
</html>
條件指令測試用例:
這裡我們對測試用例進行了封裝,後面的測試用例都将調用 initTemplate 來進行測試
@Test
public void condition() throws IOException, TemplateException {
Map root = new HashMap();
root.put("c", "c");
root.put("d", "d");
initTemplate("condition",root);
}
public void initTemplate(String templateName,Map root) throws IOException, TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
//指定模闆檔案的來源
String path = FreemarkerDemo.class.getClassLoader().getResource("template").getPath();
cfg.setDirectoryForTemplateLoading(new File(path));
//這是模闆的編碼
cfg.setDefaultEncoding("UTF-8");
//擷取模闆
Template template = cfg.getTemplate(templateName+".ftl");
//建立FreeMarker的資料模型
//這是輸出檔案
File file = new File("D://" +templateName+".html");
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
//将模闆與資料模型合并
template.process(root, out);
out.flush();
out.close();
}
5 freemarker list 指令
我們可以通過 <#list 序列 as item> 來進行序列的周遊。另外list 還有一些内置的序列的函數
?size:序列的數量
_index :序列中元素的角标
_has_next:是否是目前疊代循環中的最後一項
?sort:對序列中的元素進行排序
?sort_by:根據執行個體中的具體的否個字段進行排序
?is_enumerable:是否是集合
測試list模闆内容
<html>
<head>
<title>list 指令介紹!</ title>
</head>
<body>
<#list 0..5 as item>
${item}
</#list>
-----------------------------
_index:交标值 ,?size 擷取集合的長度
list的長度:${wordList?size}
<#list wordList as word>
目前的交标是:${word_index}值是:${word}
</#list>
--------------------------------
_has_next:是否是目前疊代循環中的最後一項
<#list wordList as word>
<#if word_has_next>
不是最後一項:${word},
<#else>
是最後一項:${word}
</#if>
</#list>
---------------------------------
字元串(按首字母排序),數字,日期值
正常周遊
<#list wordList as word>
${word}
</#list>
升序
<#list wordList?sort as word>
${word}
</#list>
降序
<#list wordList?sort?reverse as word>
${word}
</#list>
反向周遊
<#list wordList?reverse as word>
${word}
</#list>
-------------------------------------
正常周遊
<#list productList as p>
${p.name}#${p.url}#${p.saleNum}
</#list>
升序
<#list productList?sort_by("saleNum") as p>
${p.name}#${p.url}#${p.saleNum}
</#list>
降序
<#list productList?sort_by("saleNum")?reverse as p>
${p.name}#${p.url}#${p.saleNum}
</#list>
反向周遊
<#list productList?reverse as p>
${p.name}#${p.url}#${p.saleNum}
</#list>
---------------------------------------
<#list map?keys as item>
<#if (item == "productMap3")>
<#list map[item] as p>
${p.name}#${p.url}#${p.saleNum}
</#list>
<#else>
${map[item]}
</#if>
</#list>
----------------------------------------
?is_string:是否是字元串
<#list map?keys as item>
<#if map[item]?is_string>
${map[item]}
<#else>
<#list map[item] as p>
${p.name}#${p.url}#${p.saleNum}
</#list>
</#if>
</#list>
-----------------------------------------
?is_enumerable:是否是集合
<#list map?keys as item>
<#if map[item]?is_enumerable>
<#list map[item] as p>
${p.name}#${p.url}#${p.saleNum}
</#list>
<#else>
${map[item]}
</#if>
</#list>
</body>
</html>
list模闆内容測試用例:
@Test
public void list() throws IOException, TemplateException {
Map root = new HashMap();
List wordList = new ArrayList();
wordList.add(5);
wordList.add(3);
wordList.add(6);
root.put("wordList", wordList);
List<Product> productList = new ArrayList<Product>();
productList.add(new Product("123.html", "蘋果", 5));
productList.add(new Product("13.html", "香蕉", 3));
productList.add(new Product("13.html", "芒果", 15));
root.put("productList", productList);
Map map = new HashMap();
map.put("productMap", "a");
map.put("productMap2", "b");
map.put("productMap3", productList);
root.put("map", map);
initTemplate("list",root);
}
list模闆内容測試結果:
<html>
<head>
<title>list 指令介紹!</ title>
</head>
<body>
0
1
2
3
4
5
-----------------------------
_index:交标值 ,?size 擷取集合的長度
list的長度:3
目前的交标是:0值是:5
目前的交标是:1值是:3
目前的交标是:2值是:6
--------------------------------
_has_next:是否是目前疊代循環中的最後一項
不是最後一項:5,
不是最後一項:3,
是最後一項:6
---------------------------------
字元串(按首字母排序),數字,日期值
正常周遊
5
3
6
升序
3
5
6
降序
6
5
3
反向周遊
6
3
5
-------------------------------------
正常周遊
蘋果#123.html#5
香蕉#13.html#3
芒果#13.html#15
升序
香蕉#13.html#3
蘋果#123.html#5
芒果#13.html#15
降序
芒果#13.html#15
蘋果#123.html#5
香蕉#13.html#3
反向周遊
芒果#13.html#15
香蕉#13.html#3
蘋果#123.html#5
---------------------------------------
a
b
蘋果#123.html#5
香蕉#13.html#3
芒果#13.html#15
----------------------------------------
a
b
蘋果#123.html#5
香蕉#13.html#3
芒果#13.html#15
-----------------------------------------
a
b
蘋果#123.html#5
香蕉#13.html#3
芒果#13.html#15
</body>
</html>
6 freemarker assign指令
我們可以通過 assign指令在ftl中進行值的定義
assign指令測試模闆内容:
<#assign name="zhuoqianmingyue">
${name}
-----------------------------------------------------------
<#assign product={"name":"蘋果","url":"123.html","saleNum":23} >
${product.name},${product.url},${product.saleNum}
assign指令測試用例:
@Test
public void assign() throws IOException, TemplateException {
Map root = new HashMap();
initTemplate("assign",root);
}
assign指令測試結果:
7 建立宏
這裡的宏 我們可以了解成建立方法。我們可以通過 <#macro>标簽來定義宏。
建立宏模闆内容如下
建立宏模闆測試用例:
@Test
public void marco() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("macroShow", map);
}
建立宏模闆測試結果:
嵌套指令<#nested> 可以執行兩次相同的調用
@Test
public void nested() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("nestedShow", map);
}
8 include指令
include引入的檔案内容freemarker将解析其中的freemarker文法并移交給模闆,同時assign的值可以互相調用
include 引入ftl模闆
parent.ftl
<html>
<body>
我是公共的頁面 ${who}引用啦我!
</body>
</html>
include.ftl
我是include頁面
<#assign who="include.ftl">
<#include "parent.ftl"/>
include指令的測試用例:
@Test
public void include() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("include", map);
}
include指令的測試結果:
include 引入html
在resource 目錄下建立include.html
模闆内容:
測試用例:
@Test
public void include2() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("include2", map);
}
測試結果:
9 import指令
import引入的檔案内容freemarker将不會解析其中的freemarker文法,同時assign的值可以可以互相調用。他可以建立一個命名空間 通過該命名調用import 模闆中的變量和宏
@Test
public void importFun() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("importFun", map);
}
import 使用宏
@Test
public void macroImportShow() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("macroImportShow", map);
}
10 freemarker 處理不存在或則為null的值
當資料模型的key不存在或者key 的value是null 時 我們執行模闆引擎進行渲染的化或報如下圖的錯誤
我們可以通過在flt頁面中使用! 或者 ?if_exists 使其不做任何顯示 通過我們也可以通過 ?if_exists 和??指令進行是否為空的判斷。
測試模闆内容:
${word!}
${word?if_exists}
${product?if_exists.name?if_exists}
<#if word??>
<#else>
word的值為空
</#if>
<#if word?if_exists>
<#else>
word的值為空
</#if>
測試用例:
@Test
public void ifExists() throws IOException, TemplateException {
Map root = new HashMap();
String word = null;
root.put("word", word);
Product product = new Product();
product.setName(null);
root.put("product", product);
initTemplate("ifExists",root);
}
測試結果:
11 freemarker 内置函數
具體請參考:https://freemarker.apache.org/docs/ref_builtins.html
我們這裡介紹一下經常使用的内置函數 ,我們上面在介紹list的時候 使用的 ?size就隻一個内置函數
11.1字元串
?length :字元串的長度 例如:${"zhuoqianmingyue"?length}
?index_of :字元串中字元的位置交标 例如:${"zhuoqianmingyue"?index_of('yue')}
?substring:截取字元串 例如:${"zhuoqianmingyue"?substring(1)} ${"zhuoqianmingyue"?substring(1,2)}
?trim:去掉字元串的空格 例如:${" Hello world "?trim}
?contains:是否包含否個字元 例如:${"Hello world "?contains('Hello')?string}
?date:日期的轉換 ,?datetime datetime的轉換
<#assign date1="2009-10-12"?date("yyyy-MM-dd")>
<#assign date2="09:28:20"?datetime("HH:mm:ss")>
?string:字元串格式輸出 例如:${date1?string}
?is_string:是否是字元串 例如:${date1?is_string?string}
以上文法模闆内容
length: ${"zhuoqianmingyue"?length}
index_of: ${"zhuoqianmingyue"?index_of('yue')}
substring: ${"zhuoqianmingyue"?substring(1)} ${"zhuoqianmingyue"?substring(1,2)}
trim: ${" Hello world "?trim}
contains:${"Hello world "?contains('Hello')?string}
<#assign date1="2009-10-12"?date("yyyy-MM-dd")>
<#assign date2="09:28:20"?datetime("HH:mm:ss")>
${date1?is_string?string}
${date1?string}
${date2?string}
測試用例:
@Test
public void string() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("string", map);
}
測試結果:
11.2 日期
.now 擷取目前時間
日期格式轉換
<#assign aDateTime = .now>
${.now}
${aDateTime?string["dd.MM.yyyy, HH:mm"]}
${aDateTime?string["EEEE, MMMM dd, yyyy, hh:mm a '('zzz')'"]}
${aDateTime?string["EEE, MMM d, ''yy"]}
${aDateTime?string.yyyy}
@Test
public void date() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("date", map);
}
11.3 序列 (Sequence)
?size ?reverse ?sort ?sort_by 我們已經在list 指令進行了示範 這裡就不在做介紹了
?chunk:序列分塊周遊
?first ?last:擷取序列中的第一個和最後一個元素
?join:拼接序列中的内容
?seq_contains:序列中是否包含某個元素
?seq_index_of:序列中元素的交标
<#assign seq = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']>
<#list seq?chunk(4) as row>
<#list row as cell>${cell} </#list>
</#list>
<#list seq?chunk(4, '-') as row>
<#list row as cell>${cell} </#list>
</#list>
---------------------------------------------
${seq[1]}
${seq?first} ${seq?last}
----------------------------------------------
<#assign colors = ["red", "green", "blue"]>
${colors?join(", ")}
---------------------------------------------
<#assign x = ["red", 16, "blue", "cyan"]>
"blue": ${x?seq_contains("blue")?string("yes", "no")}
"yellow": ${x?seq_contains("yellow")?string("yes", "no")}
16: ${x?seq_contains(16)?string("yes", "no")}
"16": ${x?seq_contains("16")?string("yes", "no")}
-------------------------------------------------
<#assign colors = ["red", "green", "blue"]>
${colors?seq_index_of("blue")}
${colors?seq_index_of("red")}
${colors?seq_index_of("purple")}
測試用例:
@Test
public void sequences() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("sequences", map);
}
11.4 數字:
abs:絕對值 2.3.20
round:四舍五入
floor:向下取整
ceiling:向上取整
string.number:整數數字輸出
string.currency:貨币格式輸出
string.percent:百分數格式輸出
?string["0.##"]:數字顯示2為小數
?string["000.00"]:小數左面不夠0補齊
${-5?abs}
${4.5?round} ${4.4?round}
${4.5?floor} ${4.5?ceiling}
<#assign x = 42>
${x}
${x?string}
${x?string.number}
${x?string.currency}
${x?string.percent}
${1.2345?string["0.##"]}
${1.2345?string["000.00"]}
@Test
public void number() throws IOException, TemplateException {
Map map = new HashMap();
initTemplate("number", map);
}
11.5 has_api
下面的内置函數傳回的結果都是布爾型的
is_string :是否是String
is_number :是否是數字
is_boolean :是否是布爾類型
is_date :是否是日期
is_macro :是否是宏
is_sequence:是否是序列