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ù)模型:
模板:
輸出:
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?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;
}
}