傳智健康項目
- 黑馬程序員-傳智健康項目(第一章)
- 黑馬程序員-傳智健康項目(第二章)
- 黑馬程序員-傳智健康項目(第三章)
- 黑馬程序員-傳智健康項目(第四章)
- 黑馬程序員-傳智健康項目(第五章)
- 黑馬程序員-傳智健康項目(第六章)
- 黑馬程序員-傳智健康項目(第七章)
- 黑馬程序員-傳智健康項目(第八章)
- 黑馬程序員-傳智健康項目(第九章)
- 黑馬程序員-傳智健康項目(第十章)
- 黑馬程序員-傳智健康項目(第十一章)
- 黑馬程序員-傳智健康項目(第十二章)
- 黑馬程序員-傳智健康項目(第十三章)
- 黑馬程序員-傳智健康項目資料
傳智健康項目 第7章
1. 頁面靜態化介紹
本章課程中我們已經實現了移動端套餐列表頁面和套餐詳情頁面的動態展示。但是我們需要思考一個問題,就是對於這兩個頁面來說,每次用戶訪問這兩個頁面都需要查詢數據庫獲取動態數據進行展示,而且這兩個頁面的訪問量是比較大的,這就對數據庫造成了很大的訪問壓力,並且數據庫中的數據變化頻率並不高。那我們需要通過什么方法為數據庫減壓並提高系統運行性能呢?答案就是頁面靜態化。
頁面靜態化其實就是將原來的動態網頁(例如通過ajax請求動態獲取數據庫中的數據並展示的網頁)改為通過靜態化技術生成的靜態網頁,這樣用戶在訪問網頁時,服務器直接給用戶響應靜態html頁面,沒有了動態查詢數據庫的過程。
那么這些靜態HTML頁面還需要我們自己去編寫嗎?其實並不需要,我們可以通過專門的頁面靜態化技術幫我們生成所需的靜態HTML頁面,例如:Freemarker、thymeleaf等。
2. Freemarker介紹
FreeMarker 是一個用 Java 語言編寫的模板引擎,它基於模板來生成文本輸出。FreeMarker與 Web 容器無關,即在 Web 運行時,它並不知道 Servlet 或 HTTP。它不僅可以用作表現層的實現技術,而且還可以用於生成 XML,JSP 或 Java 等。

3. Freemarker入門案例
3.1 環境搭建
創建maven工程並導入Freemarker的maven坐標
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
3.2 創建模板文件
模板文件中有四種元素:
1、文本,直接輸出的部分
2、注釋,即<#--...-->格式不會輸出
3、插值(Interpolation):即${..}部分,將使用數據模型中的部分替代輸出
4、FTL指令:FreeMarker指令,和HTML標記類似,名字前加#予以區分,不會輸出
Freemarker的模板文件后綴可以任意,一般建議為ftl。
在D盤創建ftl目錄,在ftl目錄中創建名稱為test.ftl的模板文件,內容如下:
<html>
<head>
<meta charset="utf-8">
<title>Freemarker入門</title>
</head>
<body>
<#--我只是一個注釋,我不會有任何輸出 -->
${name}你好,${message}
</body>
</html>
3.3 生成文件
使用步驟:
第一步:創建一個 Configuration 對象,直接 new 一個對象。構造方法的參數就是 freemarker的版本號。
第二步:設置模板文件所在的路徑。
第三步:設置模板文件使用的字符集。一般就是 utf-8。
第四步:加載一個模板,創建一個模板對象。
第五步:創建一個模板使用的數據集,可以是 pojo 也可以是 map。一般是 Map。
第六步:創建一個 Writer 對象,一般創建 FileWriter 對象,指定生成的文件名。
第七步:調用模板對象的 process 方法輸出文件。
第八步:關閉流。
public static void main(String[] args) throws Exception{
//1.創建配置類
Configuration configuration=new Configuration(Configuration.getVersion());
//2.設置模板所在的目錄
configuration.setDirectoryForTemplateLoading(new File("D:\\ftl"));
//3.設置字符集
configuration.setDefaultEncoding("utf-8");
//4.加載模板
Template template = configuration.getTemplate("test.ftl");
//5.創建數據模型
Map map=new HashMap();
map.put("name", "張三");
map.put("message", "歡迎來到傳智播客!");
//6.創建Writer對象
Writer out =new FileWriter(new File("d:\\test.html"));
//7.輸出
template.process(map, out);
//8.關閉Writer對象
out.close();
}
上面的入門案例中Configuration配置對象是自己創建的,字符集和模板文件所在目錄也是在Java代碼中指定的。在項目中應用時可以將Configuration對象的創建交由Spring框架來完成,並通過依賴注入方式將字符集和模板所在目錄注入進去。
4. Freemarker指令
4.1 assign指令
assign指令用於在頁面上定義一個變量
(1)定義簡單類型
<#assign linkman="周先生">
聯系人:${linkman}
(2)定義對象類型
<#assign info={"mobile":"13812345678",'address':'北京市昌平區'} >
電話:${info.mobile} 地址:${info.address}
4.2 include指令
include指令用於模板文件的嵌套
(1)創建模板文件head.ftl
<h1>黑馬程序員</h1>
(2)修改入門案例中的test.ftl,在test.ftl模板文件中使用include指令引入上面的模板文件
<#include "head.ftl"/>
4.3 if指令
if指令用於判斷
(1)在模板文件中使用if指令進行判斷
<#if success=true>
你已通過實名認證
<#else>
你未通過實名認證
</#if>
(2)在java代碼中為success變量賦值
map.put("success", true);
在freemarker的判斷中,可以使用= 也可以使用==
4.4 list指令
list指令用於遍歷
(1)在模板文件中使用list指令進行遍歷
<#list goodsList as goods>
商品名稱: ${goods.name} 價格:${goods.price}<br>
</#list>
(2)在java代碼中為goodsList賦值
List goodsList=new ArrayList();
Map goods1=new HashMap();
goods1.put("name", "蘋果");
goods1.put("price", 5.8);
Map goods2=new HashMap();
goods2.put("name", "香蕉");
goods2.put("price", 2.5);
Map goods3=new HashMap();
goods3.put("name", "橘子");
goods3.put("price", 3.2);
goodsList.add(goods1);
goodsList.add(goods2);
goodsList.add(goods3);
map.put("goodsList", goodsList);
5. 生成移動端靜態頁面
前面我們已經學習了Freemarker的基本使用方法,下面我們就可以將Freemarker應用到項目中,幫我們生成移動端套餐列表靜態頁面和套餐詳情靜態頁面。接下來我們需要思考幾個問題:
(1)什么時候生成靜態頁面比較合適呢?
(2)將靜態頁面生成到什么位置呢?
(3)應該生成幾個靜態頁面呢?
對於第一個問題,應該是當套餐數據發生改變時,需要生成靜態頁面,即我們通過后台系統修改套餐數據(包括新增、刪除、編輯)時。
對於第二個問題,如果是在開發階段可以將文件生成到項目工程中,如果上線后可以將文件生成到移動端系統運行的tomcat中。
對於第三個問題,套餐列表只需要一個頁面就可以了,在這個頁面中展示所有的套餐列表數據即可。套餐詳情頁面需要有多個,即一個套餐應該對應一個靜態頁面。
5.1 環境搭建
在health_common工程的pom文件中導入Freemarker的maven坐標
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
5.2 創建模板文件
在health_service_provider工程的WEB-INF目錄中創建ftl目錄,在ftl目錄中創建模板文件mobile_setmeal.ftl和mobile_setmeal_detail.ftl文件,前者是用於生成套餐列表頁面的模板文件,后者是生成套餐詳情頁面的模板文件
(1)mobile_setmeal.ftl
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="../img/asset-favico.ico">
<title>預約</title>
<link rel="stylesheet" href="../css/page-health-order.css" />
</head>
<body data-spy="scroll" data-target="#myNavbar" data-offset="150">
<div class="app" id="app">
<!-- 頁面頭部 -->
<div class="top-header">
<span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span>
<span class="center">傳智健康</span>
<span class="f-right"><i class="icon-more"></i></span>
</div>
<!-- 頁面內容 -->
<div class="contentBox">
<div class="list-column1">
<ul class="list">
<#list setmealList as setmeal>
<li class="list-item">
<a class="link-page" href="setmeal_detail_${setmeal.id}.html">
<img class="img-object f-left"
src="http://puco9aur6.bkt.clouddn.com/${setmeal.img}"
alt="">
<div class="item-body">
<h4 class="ellipsis item-title">${setmeal.name}</h4>
<p class="ellipsis-more item-desc">${setmeal.remark}</p>
<p class="item-keywords">
<span>
<#if setmeal.sex == '0'>
性別不限
<#else>
<#if setmeal.sex == '1'>
男
<#else>
女
</#if>
</#if>
</span>
<span>${setmeal.age}</span>
</p>
</div>
</a>
</li>
</#list>
</ul>
</div>
</div>
</div>
<!-- 頁面 css js -->
<script src="../plugins/vue/vue.js"></script>
<script src="../plugins/vue/axios-0.18.0.js"></script>
</body>
注意上面模板文件中每個套餐對應的超鏈接如下:
<a class="link-page" href="setmeal_detail_${setmeal.id}.html">
可以看到,鏈接的地址是動態構成的,如果套餐的id為1,則對應的超鏈接地址為setmeal_detail_1.html;如果套餐的id為5,則對應的超鏈接地址為setmeal_detail_5.html。所以我們需要為每個套餐生成一個套餐詳情靜態頁面。
(2)mobile_setmeal_detail.ftl
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="../img/asset-favico.ico">
<title>預約詳情</title>
<link rel="stylesheet" href="../css/page-health-orderDetail.css" />
<script src="../plugins/vue/vue.js"></script>
<script src="../plugins/vue/axios-0.18.0.js"></script>
<script src="../plugins/healthmobile.js"></script>
</head>
<body data-spy="scroll" data-target="#myNavbar" data-offset="150">
<div id="app" class="app">
<!-- 頁面頭部 -->
<div class="top-header">
<span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span>
<span class="center">傳智健康</span>
<span class="f-right"><i class="icon-more"></i></span>
</div>
<!-- 頁面內容 -->
<div class="contentBox">
<div class="card">
<div class="project-img">
<img src="http://puco9aur6.bkt.clouddn.com/${setmeal.img}"
width="100%" height="100%" />
</div>
<div class="project-text">
<h4 class="tit">${setmeal.name}</h4>
<p class="subtit">${setmeal.remark}</p>
<p class="keywords">
<span>
<#if setmeal.sex == '0'>
性別不限
<#else>
<#if setmeal.sex == '1'>
男
<#else>
女
</#if>
</#if>
</span>
<span>${setmeal.age}</span>
</p>
</div>
</div>
<div class="table-listbox">
<div class="box-title">
<i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i>
<span>套餐詳情</span>
</div>
<div class="box-table">
<div class="table-title">
<div class="tit-item flex2">項目名稱</div>
<div class="tit-item flex3">項目內容</div>
<div class="tit-item flex3">項目解讀</div>
</div>
<div class="table-content">
<ul class="table-list">
<#list setmeal.checkGroups as checkgroup>
<li class="table-item">
<div class="item flex2">${checkgroup.name}</div>
<div class="item flex3">
<#list checkgroup.checkItems as checkitem>
<label>
${checkitem.name}
</label>
</#list>
</div>
<div class="item flex3">${checkgroup.remark}</div>
</li>
</#list>
</ul>
</div>
<div class="box-button">
<a @click="toOrderInfo()" class="order-btn">立即預約</a>
</div>
</div>
</div>
</div>
</div>
<script>
var vue = new Vue({
el:'#app',
methods:{
toOrderInfo(){
window.location.href = "orderInfo.html?id=${setmeal.id}";
}
}
});
</script>
</body>
5.3 配置文件
(1)在health_service_provider工程中創建屬性文件freemarker.properties
out_put_path=D:/ideaProjects/health_parent/health_mobile/src/main/webapp/pages
通過上面的配置可以指定將靜態HTML頁面生成的目錄位置
(2)在health_service_provider工程的Spring配置文件中配置
<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>
<context:property-placeholder location="classpath:freemarker.properties"/>
5.4 生成靜態頁面
修改health_service_provider工程中的SetmealServiceImpl類的add方法,加入生成靜態頁面的邏輯。
@Service(interfaceClass = SetmealService.class)
@Transactional
public class SetmealServiceImpl implements SetmealService {
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@Autowired
private SetmealDao setmealDao;
@Autowired
private JedisPool jedisPool;
@Value("${out_put_path}")//從屬性文件讀取輸出目錄的路徑
private String outputpath ;
//新增套餐,同時關聯檢查組
public void add(Setmeal setmeal, Integer[] checkgroupIds) {
setmealDao.add(setmeal);
Integer setmealId = setmeal.getId();//獲取套餐id
this.setSetmealAndCheckGroup(setmealId,checkgroupIds);
//完成數據庫操作后需要將圖片名稱保存到redis
jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES,setmeal.getImg());
//新增套餐后需要重新生成靜態頁面
generateMobileStaticHtml();
}
//生成靜態頁面
public void generateMobileStaticHtml() {
//准備模板文件中所需的數據
List<Setmeal> setmealList = this.findAll();
//生成套餐列表靜態頁面
generateMobileSetmealListHtml(setmealList);
//生成套餐詳情靜態頁面(多個)
generateMobileSetmealDetailHtml(setmealList);
}
//生成套餐列表靜態頁面
public void generateMobileSetmealListHtml(List<Setmeal> setmealList) {
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("setmealList", setmealList);
this.generateHtml("mobile_setmeal.ftl","m_setmeal.html",dataMap);
}
//生成套餐詳情靜態頁面(多個)
public void generateMobileSetmealDetailHtml(List<Setmeal> setmealList) {
for (Setmeal setmeal : setmealList) {
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("setmeal", this.findById(setmeal.getId()));
this.generateHtml("mobile_setmeal_detail.ftl",
"setmeal_detail_"+setmeal.getId()+".html",
dataMap);
}
}
public void generateHtml(String templateName,String htmlPageName,Map<String, Object> dataMap){
Configuration configuration = freeMarkerConfigurer.getConfiguration();
Writer out = null;
try {
// 加載模版文件
Template template = configuration.getTemplate(templateName);
// 生成數據
File docFile = new File(outputpath + "\\" + htmlPageName);
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
// 輸出文件
template.process(dataMap, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != out) {
out.flush();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
通過上面代碼可以看到,我們生成的套餐列表頁面名稱為m_setmeal.html,為了能夠在移動端訪問到此頁面,需要將移動端工程中的/pages/index.html頁面的超鏈接地址進行修改:
<a href="/pages/m_setmeal.html" class="link-page">
<div class="type-title">
<h3>體檢預約</h3>
<p>實時預約</p>
</div>
<div class="type-icon">
<i class="icon-zhen">
<span class="path1"></span><span class="path2"></span>
</i>
</div>
</a>
