Spring MVC 数据模型(上)

1. 前言

本节课,将和大家一起聊聊 Spring MVC 在不同的作用域中如何封装数据。

继续之前,先理解本节课程提出的数据模型概念:Spring MVC 提供的在特定作用域内封装数据的组件。掌握理解数据模型组件的使用便是全文重点。

2. 请求作用域

WEB 程序的应用层使用的是 HTTP 协议,HTTP 协议有一个特点,无状态。

所谓无状态指上一次请求与下一次请求之间是隔离的,没有内在的联系。更通俗的讲,可理解为一个患有健忘症的人,只记得当前自己在做什么,不记得自己曾经做过什么,更不会知道自己将来要做什么。

HTTP 协议的这种无状态,最初设计时是从安全角度考虑。但是,在某些应用场景下,如购物车的应用场景下,却显得无能为力。购物车中的商品不一定是一次请求下的结果,往往是多次请求下的结果。

也就是说,购物车需要保存每一次请求获取到的数据。显然,直接使用 HTTP 协议是无法做到的。就需从技术层面上提供解决方案。

原生 Servlet 提供了 3 个作用域,可以根据用户的需要来保存每一次请求过程中产生的数据。

  • 请求作用域: 使用 HttpServletRequest 组件存储的数据可以在每一次的请求周期内存在。 请求结束,数据也将消失;
  • 会话作用域: 使用 HttpSession 组件保存的数据能在整个会话生命周期内存在。如购物车就可以保存在会话作用域中;
  • 应用程序作用域: 使用 ServletContext 组件保存的数据在整个应用程序生命周期之内存在。

Spring MVC 中,把数据保存在请求作用域,或是说在整个请求过程中数据都有效。有 2 种解决方案

  • 直接使用 HttpServletRequest 组件;
  • 使用 Spriing MVC 提供的高级数据模型组件。

2.1 使用原生 HttpServletRequest

先设定一个需要请求作用域的需求场景:

需求说明: 用户登录成功后,在登录成功后的页面中显示登录者的信息。

操作流程:

  1. 准备好 3 个静态页面;
  • login.html: 登录页面,接收用户输入的用户名、密码等与登录相关的信息;
  • index.html: 登录成功后跳转到的页面;
  • fail.html: 登录失败后跳转到的页面。
  1. 编写 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>
  1. 编写响应控制器;
@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 个参数:

  • user 参数:OOP 的方式绑定请求包中传过来的数据;
  • request 参数: 注入原生 HttpServletRequest 组件。Spring 的注入功能很厉害的,只要你需要,它便能帮你拿到。登录者的信息保存在 HttpServletRequest 对象中。

Tips: HttpServletRequest 组件具有服务器端数据存储功能,本质是内部维护有一个 map 对象。HttpServletRequest 的生命周期较短,从请求开始到请求结束。所以,其保存的数据也只能在整个请求范围内有效。

  1. 编写 index.html 和 fail.html 页面。
  • idnex.html 页面内,使用 EL 表达式,读取请求作用域中的登录者信息;
<body>
我是首页
<br/>
当前登录者:${loginUser.userName}
</body>

Tips: 这里有一个坑,需要提醒一下。通过 maven 创建的 WEB 项目的 web.xml 文件的头信息版本偏低,使用 EL 表达式时,可能会在页面中出现红色的错误提示,其实并不影响发布。但终究让人看的烦心。可以找到 tomcat 服务器端的 web.xml 文件,用其头信息替换下。

  • fail.html 页面简单地加入一条 “失败” 提示语就可以了。

看一下最后的项目结构吧:

图片描述

  1. 最后的测试。

Tips: 登录的逻辑验证使用了硬代码,用户名:mk,密码:123。

登录成功后,会转到首页,且在首页中显示出登录者的信息。

图片描述

2.2 使用 Spring MVC 模型组件

直接使用 HttpServletRequest 封装请求作用域级别的数据,不能说不好。但是,Spring MVC 存在的目的,就是为了简化开发者的工作,动不动就要 Spring 把底层的原生 API 弄出来,Spring MVC 存在的意义就不大了。

HttpServletRequest 的功能是全方面的,存储数据可以说是它的副业,为了一个数据存储,把它搬出来,好像有点大材小用。

其实,你可以告诉 Spring MVC ,我只要 HttpServletRequest 的存储功能。也就是说,只要 Spring MVC 引用出 HttpServletRequest 内置的 map 对象就可以了。

图片描述

好了!现在重构一下上面的实例,仅仅修改控制器 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 个参数,user 参数就不啰嗦了。

map 参数则是一个神奇的存在。在方法中一站,等于通知 Spring MVC ,帮我把某个作用域中的 map 对象引用出来。

如前言中所述,作用域有 3 个级别,Spring MVC 怎么知道你要哪一个层面的作用域。既然没有明说,Spring MVC 引用出来的是请求作用域也就是 HttpServletRequest 的内置 map 对象。

好了,你可以放心的和前面一样测试代码,在 index.html 页面中你将看到登录者的信息。

搞了半天了,其实本质上是一样:一个直接把 HttpServletRequest 交给你用,一个仅仅只是把实现了存储的那一部分功能交给你。

有句话叫做透过现象看本质,Spring MVC 只是对原生 Servlet 开发做了高级封装。如同端午节吃棕子,礼品盒再怎么高级、漂亮,打开后还是棕子。

3. 小结

本节课和大家一起学习了 Spring MVC 中的数据模型。所谓数据模型,就是用来装数据的组件,当然,因 WEB 应用程序的特殊性,更多是要讨论存放在这个组件中的数据在一个什么样的范围之内能够被使用。

本节课学习到了,可以直接使用原生的 HttpServletRequest 完成请求作用域数据的存储,触类旁通,当然,你也可以直接使用原生 HttpSession 对象,保存会话作用域级别数据的保存。

有时,不需要为了吃一个桃子,砍掉一棵树。开发者可以使用 Spring MVC 引用出内部的存储对象,这样更小巧实用。是的,本节课程中出现的 map 是 Spring MVC 中的数据模型组件。