FreeMarker 是一個用 Java 語言編寫的模板引擎,它基於模板來生成文本輸出。FreeMarker與 Web 容器無關,即在 Web 運行時,它並不知道 Servlet 或 HTTP。它不僅可以用作表現層的實現技術,而且還可以用於生成 XML,JSP 或 Java 等。
為什么要使用網頁靜態化技術?
網頁靜態化解決方案在實際開發中運用比較多,例如新聞網站,門戶網站中的新聞頻道或者是文章類的頻道。對於電商網站的商品詳細頁來說,至少幾百萬個商品,每個商品又有大量的信息,這樣的情況同樣也適用於使用網頁靜態化來解決。
網頁靜態化技術和緩存技術的共同點都是為了減輕數據庫的訪問壓力,但是具體的應用場景不同,緩存比較適合小規模的數據,而網頁靜態化比較適合大規模且相對變化不太頻繁的數據。另外網頁靜態化還有利於SEO。
另外我們如果將網頁以純靜態化的形式展現,就可以使用Nginx這樣的高性能的web服務器來部署。Nginx可以承載5萬的並發,而Tomcat只有幾百。關於Nginx我們在后續的課程中會詳細講解。
今天我們就研究網頁靜態化技術----Freemarker 。
創建模板文件
模板文件中四種元素
1、文本,直接輸出的部分
2、注釋,即<#--...-->格式不會輸出
3、插值(Interpolation):即${..}部分,將使用數據模型中的部分替代輸出
4、FTL指令:FreeMarker指令,和HTML標記類似,名字前加#予以區分,不會輸出。
test.ftl
<html> <head> <meta charset="utf-8"> <title>Freemarker入門</title> </head> <body> <#--我只是一個注釋,我不會有任何輸出 --> ${name},你好。${message} </body> </html>
使用freemarker先導入依賴
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency>
package cn.itcats.freemarker; import java.io.File; import java.io.FileWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; import org.junit.Test; import freemarker.template.Configuration; import freemarker.template.Template; public class FreeMarkerTest { @Test public void testFreeMarker() throws Exception { //1、創建一個模板文件 //2、創建一個Configuration對象 //注意這個Configuration來自 freemarker.template Configuration configuration = new Configuration(Configuration.getVersion()); //3、設置模板文件保存的目錄 configuration.setDirectoryForTemplateLoading(new File("D:/workspaces/ftl")); //4、模板文件的編碼格式,一般就是utf-8 configuration.setDefaultEncoding("utf-8"); //5、加載一個模板文件,創建一個模板對象。 Template template = configuration.getTemplate("test.ftl"); //6、創建一個數據集。可以是pojo也可以是map。推薦使用map Map data = new HashMap<>(); data.put("test", "hello freemarker!"); //${test} Student student = new Student("001","name","廣東省"); //7、創建一個Writer對象,指定輸出文件的路徑及文件名。 Writer out = new FileWriter(new File("D:/test.html")); //8、生成靜態頁面 //@Param1:數據 @Param2:輸出流 數據data+模板test.ftl封裝到test.html template.process(data, out); //9、關閉流 out.close(); } }
注意:如未插值(即某一個${name}為設置值會拋出異常)
FTL指令:
1、assign指令
此指令用於在頁面上定義一個變量
(1)定義簡單類型:
<#assign telephone="18800088888">
聯系人:${linkman}
(2)定義對象類型:
<#assign info={"mobile":"13308886688",'address':'廣東省廣州市天河區'} >
電話:${info.mobile} 地址:${info.address}
1.1. 模板的語法
1.1.1. 訪問map中的key
${key}
1.1.2. 訪問pojo中的屬性
Student對象。學號、姓名、年齡
${key.property}
1.1.3. 取集合中的數據
<#list studentList as student> ${student.id} 或 ${studnet.name} </#list>
1.1.4. 取循環中的下標
<#list studentList as student> ${student_index} </#list>
plus:獲取集合大小
我們使用size函數來實現,代碼如下:
共 ${studentList?size} 條記錄
轉換JSON字符串為對象
<#assign text="{'bank':'交通銀行','account':'10101920201808224'}" /> <#-- 注意無法直接通過${date.bank}獲得,因為加了""引號 --> <#assign data=text?eval /> 開戶行:${data.bank} 賬號:${data.account}
1.1.5. 判斷(注意在freemarker中,==與=相同)
<#if student_index % 2 == 0> <#else> </#if>
1.1.6. 日期類型格式化
//Java代碼,代碼中對變量賦值: dataModel.put("data", new Date()); //ftl模板 系統當前時間:${data?date} //結果:2018-1-1 系統當前時間:${data?time} //結果:11:11:11 系統當前時間:${data?datetime} //結果:2018-1-1 11:11:11 自定義時間格式:${data?string("yyyy年MM月dd日 HH:mm:ss")} //結果:2018年1月1日 11:11:11
1.1.7. Null值的處理(freemarker里面不允許null值,否則拋出異常)
處理方式1: ${nullValue!} //在空值后面加! 等同於${nullValue!""} 給了默認值""
處理方式2: 判斷nullValue值是否為null
用法為:variable??,如果該變量存在,返回true,否則返回false
<#if nullValue??> <!-- true --> nullValue值不為空 <#else> <!-- false --> nullValue值為null </#id>
1.1.8. Include標簽
<#include “模板名稱.ftl”>
//注意,引入其他模板時,被引入的模板一定要有數據,否則報錯,優勢:可復用,一般網站頭尾部分相同,且利於后期維護。
1.1.9 數字轉換為字符串並去除,分隔符
代碼中對變量賦值:
map.put("point", 111111111);
修改模板:
分數 :${point}
//頁面顯示
分數 :111,111,111
我們會發現數字會以每三位一個分隔符顯示,有些時候我們不需要這個分隔符,就需要將數字轉換為字符串,使用內建函數c
分數:${point?c}
頁面顯示效果如下:
分數:111111111
1.2算數運算符
1.2.1、FreeMarker表達式中完全支持算術運算,FreeMarker支持的算術運算符包括:+, - , * , / , %
1.2.2、邏輯運算符
邏輯運算符有如下幾個: 邏輯與:&& 邏輯或:|| 邏輯非:! 邏輯運算符只能作用於布爾值,否則將產生錯誤
1.2.3、比較運算符
表達式中支持的比較運算符有如下幾個: 1 =或者==:判斷兩個值是否相等. 2 !=:判斷兩個值是否不等. 3 >或者gt:判斷左邊值是否大於右邊值 4 >=或者gte:判斷左邊值是否大於等於右邊值 5 <或者lt:判斷左邊值是否小於右邊值 6 <=或者lte:判斷左邊值是否小於等於右邊值 注意: =和!=可以用於字符串,數值和日期來比較是否相等,但=和!=兩邊必須是相同類型的值,否則會產生錯誤, 而且FreeMarker是精確比較,"x","x ","X"是不等的.其它的運行符可以作用於數字和日期,但不能作用於字符 串,大部分的時候,使用gt等字母運算符代替>會有更好的效果,因為 FreeMarker會把>解釋成FTL標簽的結束字 符,當然,也可以使用括號來避免這種情況,如:<#if (x>y)> 如使用: <#if i>10> i大於10 <#/if> //此時會報錯,使用gt替代 <#if i gt 10><#/if>就不會有問題了
2.1.1. 創建整合spring的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/ftl/" /> <property name="defaultEncoding" value="UTF-8" /> </bean> </beans>
在src/main/resource中創建 properties/page.properties
內容為:pagedir=d:\\item\\
邏輯:
1、從spring容器中獲得FreeMarkerConfigurer對象。
2、從FreeMarkerConfigurer對象中獲得Configuration對象。
3、使用Configuration對象獲得Template對象。
4、創建數據集
5、創建輸出文件的Writer對象。
6、調用模板對象的process方法,生成文件。
7、關閉流。
@Service public class ItemPageServiceImpl implements ItemPageService { @Value("${pagedir}") private String pagedir; @Autowired private FreeMarkerConfig freeMarkerConfig; @Autowired private TbGoodsMapper goodsMapper; @Autowired private TbGoodsDescMapper goodsDescMapper; //生成html public boolean genItemHtml(Long goodsId){ try { Configuration configuration = freeMarkerConfig.getConfiguration(); Template template = configuration.getTemplate("item.ftl"); Map dataModel=new HashMap<>(); //1.加載商品表數據 TbGoods goods = goodsMapper.selectByPrimaryKey(goodsId); dataModel.put("goods", goods); //2.加載商品擴展表數據 TbGoodsDesc goodsDesc = goodsDescMapper.selectByPrimaryKey(goodsId); dataModel.put("goodsDesc", goodsDesc); Writer out=new FileWriter(pagedir+goodsId+".html"); template.process(dataModel, out); out.close(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } }