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