Spring MVC 數(shù)據(jù)模型(下)
1. 前言
Spring MVC 為了解決 HTTP 協(xié)議的無狀態(tài)性,提供了很多能適用于不同作用域需求的模型對(duì)象,從而保證程序中的數(shù)據(jù)能在開發(fā)者需要的地方出現(xiàn)。
本節(jié)課繼續(xù)和大家聊聊 Spring MVC 中的數(shù)據(jù)模型組件。
- Model;
- ModelMap;
- ModelAndView。
2. 更多數(shù)據(jù)模型
模型是一個(gè)用的很廣的概念。本課程中數(shù)據(jù)模型應(yīng)該有 2 層含義:
- WEB 程序中封裝數(shù)據(jù)的組件;
- 此模型對(duì)象會(huì)有自己特定作用域。
上一小節(jié)中,向大家介紹了 Map 模型對(duì)象,其本質(zhì)是對(duì) HttpServletRequest 對(duì)象中的存儲(chǔ)功能的引用。Map 還是比較原始和天然的。Spring MVC 為了展現(xiàn)自己的特色服務(wù),對(duì) Map 進(jìn)行了多層面的封裝,提供了更豐富的數(shù)據(jù)模型對(duì)象。
Tips: 重要的事情強(qiáng)調(diào)一下,你將認(rèn)識(shí)或使用到的數(shù)據(jù)模型會(huì)很多,但其本質(zhì)是:
- 如果是請(qǐng)求作用域,你只是在間接使用 HttpServletRequest 對(duì)象;
- 如果是會(huì)話作用域,你只是在間接使用 HttpSession 對(duì)象。
2.1 Model
這個(gè)字面意思很明顯,咱就是一個(gè)模型組件。
Model 是一個(gè)接口類型,Model 接口中提供了標(biāo)準(zhǔn)的保存數(shù)據(jù)的方法。
Model addAttribute(String attributeName, @Nullable Object attributeValue);
Model 使用起來很簡(jiǎn)單,將 Model 設(shè)為控制器方法的參數(shù)便可 ,其它的交給 Spring MVC,因?yàn)?Model 是一個(gè)接口類型,Spring MVC 會(huì)為我們辨明身份,注入一個(gè)具體的實(shí)例對(duì)象。
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login01(User user,Model map) {
if("mk".equals(user.getUserName()) && "123".equals(user.getUserPassword())) {
map.addAttribute("loginUser", user);
return "index";
}
return "fail";
}
Model 接口中還有其它的方法,但都不常用,感興趣的話大家可以查閱其源代碼或 API 文檔。
2.2 ModelMap
這個(gè)組件的字面意思是說,我是用 Map 存儲(chǔ)數(shù)據(jù)的模型??梢圆殚喴幌缕湓创a結(jié)構(gòu).,本質(zhì)上還就是一個(gè)鏈表實(shí)現(xiàn)的 Map,都是一家人呀。
Map、Map …… 又見 Map。
ModelMap 中提供的方法和 Model 接口差不多,不同之處在于有自己的實(shí)現(xiàn)。
public class ModelMap extends LinkedHashMap<String, Object> {
public ModelMap addAttribute(String attributeName, @Nullable Object attributeValue) {
Assert.notNull(attributeName, "Model attribute name must not be null");
put(attributeName, attributeValue);
return this;
}
}
如何使用?
在 Spring MVC 的世界,會(huì)讓你感覺到自己的多余,因?yàn)楹芏嗍虑?Spring MVC 都幫你溫柔的解決。一樣的,只需要設(shè)定為控制器方法的參數(shù)就可以了。
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login01(User user,ModelMap map) {
if("mk".equals(user.getUserName()) && "123".equals(user.getUserPassword())) {
map.addAttribute("loginUser", user);
return "index";
}
return "fail";
}
會(huì)發(fā)現(xiàn),大家除了名字不一樣外,其它好像也沒有什么不一樣。
2.3 ModelAndView
從字面上了解這個(gè)組件,應(yīng)該是除了充當(dāng)模型的功能外,還與視圖有關(guān)系。想要徹底看透它,很簡(jiǎn)單,查閱一下源代碼結(jié)構(gòu)。
public class ModelAndView {
@Nullable
private Object view;
@Nullable
private ModelMap model;
//……
}
Tips: 搞了半天,會(huì)發(fā)現(xiàn),ModelAndView 其實(shí)就是封裝了 ModelMap 外加一個(gè)用來保存視圖相關(guān)信息的 view 對(duì)象。
其實(shí),大家都是一家人,根源是相同的,具有共同的遺傳基因,當(dāng)然,還會(huì)有只屬于自己的獨(dú)有功能。
ModelAndView 和其它的數(shù)據(jù)模型使用起來稍有一點(diǎn)不同,往往是作為控制器方法的返回值。
@RequestMapping(value="/login",method=RequestMethod.POST)
public ModelAndView login01(User user) {
ModelAndView mv=new ModelAndView();
if("mk".equals(user.getUserName()) && "123".equals(user.getUserPassword())) {
mv.addObject("loginUser", user);
mv.setViewName("index");
return mv;
}
mv.setViewName("fail");
return mv;
}
Tips: ModelAndView 把數(shù)據(jù)模型和視圖拼在了一起,便于一次性交給前端控制器處理。
3. 會(huì)話作用域
Map、Model 、ModelMap、ModelAndView 這幾個(gè)數(shù)據(jù)模型組件,默認(rèn)情況下,其中所保存的數(shù)據(jù)都是請(qǐng)求作用域級(jí)別的。
在很多應(yīng)用場(chǎng)景下,需要數(shù)據(jù)在整個(gè)會(huì)話過程中都能訪問到。比如登錄者信息、購(gòu)物車信息等。
面對(duì)這種數(shù)據(jù)需求時(shí),Spring MVC 又如何實(shí)現(xiàn)?
你能想到的,Spring MVC 早就想到了。使用 @SessionAttributes 注解即可。
@SessionAttributes 注解是類級(jí)別的注解,需要添加在控制器類的前面。
@Controller
@RequestMapping("/user")
@SessionAttributes("loginUser")
public class UserAction {
@RequestMapping(value="/login",method=RequestMethod.POST)
public ModelAndView login01(User user) {
ModelAndView mv=new ModelAndView();
if("mk".equals(user.getUserName()) && "123".equals(user.getUserPassword())) {
mv.addObject("loginUser", user);
mv.setViewName("index");
return mv;
}
mv.setViewName("fail");
return mv;
}
}
Tips: @SessionAttributes(“l(fā)oginUser”) 中的屬性名 loginUser 必須保持和 mv.addObject(“l(fā)oginUser”, user); 中的 loginUser 名一樣。
從底層思維來講, Spring MVC 即把數(shù)據(jù)保存到請(qǐng)求作用域中、也保存到會(huì)話作用域中。
測(cè)試時(shí),只需要在 index.jsp 中添加如下面的 EL 表達(dá)式,指明數(shù)據(jù)的作用域。
<body>
我是首頁(yè)
<br/>
請(qǐng)求作用域中得到當(dāng)前登錄者:${requestScope.loginUser.userName}
<br/>
會(huì)話作用域中得到當(dāng)前登錄者:${sessionScope.loginUser.userName}
</body>
啟動(dòng)瀏覽器,打開登錄頁(yè)面,輸入登錄名、登錄密碼、點(diǎn)擊登錄,然后在瀏覽器中會(huì)看到無論是請(qǐng)求作用域、還是會(huì)話作用域中都可以獲取到登錄者的信息。
4. 小結(jié)
本節(jié)課程和大家一起講解了 Model 、ModelMap、ModelAndView 數(shù)據(jù)模型,并講解了可以使用 @SessionAttributes 注解提升數(shù)據(jù)模型的作用域。
從功能層面上講,所有數(shù)據(jù)模型組件的功能是一樣的。從底層上講,區(qū)別在于實(shí)現(xiàn) Map 的數(shù)據(jù)結(jié)構(gòu)。
- 直接使用 Map 做數(shù)據(jù)模型時(shí), Spring MVC 注入的是一個(gè) LinkedHashMap 實(shí)例。
- ModelMap 是 LinkedHashMap 的子類,本質(zhì)還是 LinkedHashMap,只是封裝了 put 方法,讓其顯得高級(jí)一點(diǎn)。
- Model 是接口,Spring MVC 注入的也是 LinkedHashMap 實(shí)例。
所以,無論是使用 Map 、Model 還是 ModelMap 其實(shí)都是在借助 LinkedHashMap 完成數(shù)據(jù)的存儲(chǔ)。其區(qū)別在于應(yīng)用場(chǎng)景,有時(shí),需要接口注入,有時(shí)可能需要類注入。當(dāng)然,還會(huì)有自己的功能實(shí)現(xiàn)。