Spring MVC 數(shù)據(jù)模型(上)
1. 前言
本節(jié)課,將和大家一起聊聊 Spring MVC 在不同的作用域中如何封裝數(shù)據(jù)。
繼續(xù)之前,先理解本節(jié)課程提出的數(shù)據(jù)模型概念:Spring MVC 提供的在特定作用域內(nèi)封裝數(shù)據(jù)的組件。掌握理解數(shù)據(jù)模型組件的使用便是全文重點(diǎn)。
2. 請求作用域
WEB 程序的應(yīng)用層使用的是 HTTP 協(xié)議,HTTP 協(xié)議有一個(gè)特點(diǎn),無狀態(tài)。
所謂無狀態(tài)指上一次請求與下一次請求之間是隔離的,沒有內(nèi)在的聯(lián)系。更通俗的講,可理解為一個(gè)患有健忘癥的人,只記得當(dāng)前自己在做什么,不記得自己曾經(jīng)做過什么,更不會知道自己將來要做什么。
HTTP 協(xié)議的這種無狀態(tài),最初設(shè)計(jì)時(shí)是從安全角度考慮。但是,在某些應(yīng)用場景下,如購物車的應(yīng)用場景下,卻顯得無能為力。購物車中的商品不一定是一次請求下的結(jié)果,往往是多次請求下的結(jié)果。
也就是說,購物車需要保存每一次請求獲取到的數(shù)據(jù)。顯然,直接使用 HTTP 協(xié)議是無法做到的。就需從技術(shù)層面上提供解決方案。
原生 Servlet 提供了 3 個(gè)作用域,可以根據(jù)用戶的需要來保存每一次請求過程中產(chǎn)生的數(shù)據(jù)。
- 請求作用域: 使用 HttpServletRequest 組件存儲的數(shù)據(jù)可以在每一次的請求周期內(nèi)存在。 請求結(jié)束,數(shù)據(jù)也將消失;
- 會話作用域: 使用 HttpSession 組件保存的數(shù)據(jù)能在整個(gè)會話生命周期內(nèi)存在。如購物車就可以保存在會話作用域中;
- 應(yīng)用程序作用域: 使用 ServletContext 組件保存的數(shù)據(jù)在整個(gè)應(yīng)用程序生命周期之內(nèi)存在。
Spring MVC 中,把數(shù)據(jù)保存在請求作用域,或是說在整個(gè)請求過程中數(shù)據(jù)都有效。有 2 種解決方案
- 直接使用 HttpServletRequest 組件;
- 使用 Spriing MVC 提供的高級數(shù)據(jù)模型組件。
2.1 使用原生 HttpServletRequest
先設(shè)定一個(gè)需要請求作用域的需求場景:
需求說明: 用戶登錄成功后,在登錄成功后的頁面中顯示登錄者的信息。
操作流程:
- 準(zhǔn)備好 3 個(gè)靜態(tài)頁面;
- login.html: 登錄頁面,接收用戶輸入的用戶名、密碼等與登錄相關(guān)的信息;
- index.html: 登錄成功后跳轉(zhuǎn)到的頁面;
- fail.html: 登錄失敗后跳轉(zhuǎn)到的頁面。
- 編寫 login.html 頁面中的登錄表單;
<form action="user/login" method="post">
姓名:<input name="userName" value="" type="text"> <br />
密碼:<input name="userPassword" value="" type="password">
<br />
<input name="btnLogin" value="登錄" type="submit">
<input name="btnRe" value="重置" type="reset">
</form>
- 編寫響應(yīng)控制器;
@RequestMapping("/login")
public String login(User user,HttpServletRequest request) {
if("mk".equals(user.getUserName()) && "123".equals(user.getUserPassword())) {
//請求作用域
request.setAttribute("loginUser", user);
return "index";
}
return "fail";
}
login()方法有 2 個(gè)參數(shù):
- user 參數(shù): 以 OOP 的方式綁定請求包中傳過來的數(shù)據(jù);
- request 參數(shù): 注入原生 HttpServletRequest 組件。Spring 的注入功能很厲害的,只要你需要,它便能幫你拿到。登錄者的信息保存在 HttpServletRequest 對象中。
Tips: HttpServletRequest 組件具有服務(wù)器端數(shù)據(jù)存儲功能,本質(zhì)是內(nèi)部維護(hù)有一個(gè) map 對象。HttpServletRequest 的生命周期較短,從請求開始到請求結(jié)束。所以,其保存的數(shù)據(jù)也只能在整個(gè)請求范圍內(nèi)有效。
- 編寫 index.html 和 fail.html 頁面。
- idnex.html 頁面內(nèi),使用 EL 表達(dá)式,讀取請求作用域中的登錄者信息;
<body>
我是首頁
<br/>
當(dāng)前登錄者:${loginUser.userName}
</body>
Tips: 這里有一個(gè)坑,需要提醒一下。通過 maven 創(chuàng)建的 WEB 項(xiàng)目的 web.xml 文件的頭信息版本偏低,使用 EL 表達(dá)式時(shí),可能會在頁面中出現(xiàn)紅色的錯誤提示,其實(shí)并不影響發(fā)布。但終究讓人看的煩心??梢哉业?tomcat 服務(wù)器端的 web.xml 文件,用其頭信息替換下。
- fail.html 頁面簡單地加入一條 “失敗” 提示語就可以了。
看一下最后的項(xiàng)目結(jié)構(gòu)吧:
- 最后的測試。
-
啟動瀏覽器,在地址欄中輸入 http://localhost:8888/sm-demo/login.html 。打開登錄頁面;
-
在登錄頁面中輸入用戶名和密碼,點(diǎn)擊登錄按鈕。
Tips: 登錄的邏輯驗(yàn)證使用了硬代碼,用戶名:mk,密碼:123。
登錄成功后,會轉(zhuǎn)到首頁,且在首頁中顯示出登錄者的信息。
2.2 使用 Spring MVC 模型組件
直接使用 HttpServletRequest 封裝請求作用域級別的數(shù)據(jù),不能說不好。但是,Spring MVC 存在的目的,就是為了簡化開發(fā)者的工作,動不動就要 Spring 把底層的原生 API 弄出來,Spring MVC 存在的意義就不大了。
HttpServletRequest 的功能是全方面的,存儲數(shù)據(jù)可以說是它的副業(yè),為了一個(gè)數(shù)據(jù)存儲,把它搬出來,好像有點(diǎn)大材小用。
其實(shí),你可以告訴 Spring MVC ,我只要 HttpServletRequest 的存儲功能。也就是說,只要 Spring MVC 引用出 HttpServletRequest 內(nèi)置的 map 對象就可以了。
好了!現(xiàn)在重構(gòu)一下上面的實(shí)例,僅僅修改控制器 login()方法的代碼。
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(User user,Map map) {
if("mk".equals(user.getUserName()) && "123".equals(user.getUserPassword())) {
map.put("loginUser", user);
return "index";
}
return "fail";
}
還是 2 個(gè)參數(shù),user 參數(shù)就不啰嗦了。
map 參數(shù)則是一個(gè)神奇的存在。在方法中一站,等于通知 Spring MVC ,幫我把某個(gè)作用域中的 map 對象引用出來。
如前言中所述,作用域有 3 個(gè)級別,Spring MVC 怎么知道你要哪一個(gè)層面的作用域。既然沒有明說,Spring MVC 引用出來的是請求作用域也就是 HttpServletRequest 的內(nèi)置 map 對象。
好了,你可以放心的和前面一樣測試代碼,在 index.html 頁面中你將看到登錄者的信息。
搞了半天了,其實(shí)本質(zhì)上是一樣:一個(gè)直接把 HttpServletRequest 交給你用,一個(gè)僅僅只是把實(shí)現(xiàn)了存儲的那一部分功能交給你。
有句話叫做透過現(xiàn)象看本質(zhì),Spring MVC 只是對原生 Servlet 開發(fā)做了高級封裝。如同端午節(jié)吃棕子,禮品盒再怎么高級、漂亮,打開后還是棕子。
3. 小結(jié)
本節(jié)課和大家一起學(xué)習(xí)了 Spring MVC 中的數(shù)據(jù)模型。所謂數(shù)據(jù)模型,就是用來裝數(shù)據(jù)的組件,當(dāng)然,因 WEB 應(yīng)用程序的特殊性,更多是要討論存放在這個(gè)組件中的數(shù)據(jù)在一個(gè)什么樣的范圍之內(nèi)能夠被使用。
本節(jié)課學(xué)習(xí)到了,可以直接使用原生的 HttpServletRequest 完成請求作用域數(shù)據(jù)的存儲,觸類旁通,當(dāng)然,你也可以直接使用原生 HttpSession 對象,保存會話作用域級別數(shù)據(jù)的保存。
有時(shí),不需要為了吃一個(gè)桃子,砍掉一棵樹。開發(fā)者可以使用 Spring MVC 引用出內(nèi)部的存儲對象,這樣更小巧實(shí)用。是的,本節(jié)課程中出現(xiàn)的 map 是 Spring MVC 中的數(shù)據(jù)模型組件。