Spring MVC 模型数据绑定
Spring MVC 框架可以快速地搭建起一个基于 MVC 的 Web 应用程序,但 JavaWeb 的底层仍然是基于 Servlet 的形式。
SpringMVC 与传统 Servlet 的比较:
-
Spring MVC 首先解决的问题是 Servlet 业务方法的局限性;
每个 Servlet 都要实现 HttpServlet 抽象类,才能具备接收和处理 http 的请求的功能;每个 Servlet 只有一个 doGet 和 doPost 方法。如果客户端向服务器发送了 10 个 GET 类型的请求,我们要么写 10 个 Servlet 去处理请求,要么在 1 个 Servlet 的 1 个 doGet 方法中完成 10 种不同的处理逻辑。
-
Spring MVC 将 Servlet 的功能做了调整,Servlet 只负责请求的接收与分发(DispatcherServlet),而不负责具体业务实现,将请求分发给不同的 Handler,不需要实现任何接口,完全可以根据业务逻辑来自己定义。
SpringMVC 很重要的一项工作是在控制器获取模型数据并返回给客户端,在 JSP 页面展示模型数据,使用的技术是通过 EL 表达式从域对象中取值。
在 Servlet 中我们可以直接调用 web 资源给域对象传值,模型数据的绑定,是指将模型数据绑定给 JSP 域对象,JSP 四大作用域对应的四大内置对象分别是:pageContext、request、session、application。
模型数据的绑定是由 ViewResolver 来完成的,开发时,我们先添加模型数据,再将绑定的工作交由ViewResolver 视图解析器来处理。
Spring MVC 提供了多种方式添加模型数据:
- Map
- Model
- ModelAndView
- @ModelAttribute
- @SessionAttribute
开发中经常用到的域对象是 request 和 session,所以针对这两个域对象进行讲解,pageContext 和 application 可以通过获取原生 Servlet 资源的方式进行绑定,在实际开发中使用不多。
原生 Servlet API 绑定数据
直接使用原生的 Servlet API 绑定模型数据,SpringMVC 可以直接在业务方法获取到 Servlet 原生的 web 资源,比如:HttpServletRequest 和 HttpSession。
首先在 pom.xml 文件中添加原生 Servlet 的依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
创建实体类 User:
public class User {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
创建视图层 view
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<html>
<head>
<title>数据绑定</title>
</head>
<body>
欢迎您!学号:${requestScope.user.id} 用户名:${requestScope.user.name}
<hr/>
欢迎您!学号:${sessionScope.user.id} 用户名:${sessionScope.user.name}
<hr/>
欢迎您!学号:${applicationScope.user.id} 用户名:${applicationScope.user.name}
</body>
</html>
将模型数据绑定到 request 对象
在方法定义时添加 HttpServletRequest 入参即可,在方法体中,可直接对 request 对象进行操作。
@RequestMapping("/req")
public String req(HttpServletRequest request){
User user = new User();
user.setId(10);
user.setName("张三");
request.setAttribute("user",user);
return "view";
}
将模型数据绑定到 session 对象
@RequestMapping("/sess")
public String sess(HttpServletRequest request){
HttpSession session = request.getSession();
User user = new User();
user.setId(10);
user.setName("张三");
session.setAttribute("user",user);
return "view";
}
@RequestMapping("/session")
public String session(HttpSession session){
User user = new User();
user.setId(10);
user.setName("张三");
session.setAttribute("user",user);
return "view";
}
将模型数据绑定到 application 对象
@RequestMapping("application")
public String application(HttpServletRequest request){
//获取application对象
ServletContext application = request.getServletContext();
User user = new User();
user.setId(10);
user.setName("张三");
//绑定数据
application.setAttribute("user",user);
return "view";
}
1. Map
Spring MVC 在内部使用 Model 接口存储模型数据,在调用业务方法前会创建一个隐含对象作为模型数据的存储容器。设置业务方法的入参为 Map 类型,SpringMVC 会将隐含对象的引用传递给入参。
在方法体中,开发者可以通过入参对象访问到模型中的所有数据,也可以向模型中添加新数据。
只需要在业务方法添加 Map 类型的入参,方法体中可以通过对入参的操作来完成模型数据的添加。
@RequestMapping("/map")
public String map(Map<String, Object> map){
User user = new User();
user.setId(12);
user.setName("李四");
//绑定模型数据
map.put("user",user);
return "view";
}
业务方法执行完后,返回模型数据和视图信息给 DispatcherServlet,DispatcherServlet 通过 ViewResolver 对视图信息进行解析,逻辑视图映射到物理视图,同时将模型数据绑定到 JSP 的 request 域对象中,在 JSP 页面,可以直接通过 EL 表达式取值。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<html>
<head>
<title>数据绑定</title>
</head>
<body>
欢迎您!学号:${user.id} 用户名:${user.name}
</body>
</html>
启动 Tomcat 服务器,运行程序:
2. Model
Model 与 Map 类似,业务方法通过添加 Model 类型的入参来完成模型数据的绑定。
@RequestMapping("/model")
public String model(Model model){
User user = new User();
user.setId(12);
user.setName("李四");
//绑定模型数据
model.addAttribute("user",user);
return "view";
}
3. ModelAndView
与 Map 和 Model 不同的是,ModelAndView 不但包含模型数据,同时也包含了视图信息,所以使用 ModelAndView 来处理模型数据,业务方法的返回值必须是 ModelAndView。
Handler 的业务方法中对 ModelAndView 进行两个操作:
- 填充模型数据;
- 绑定视图信息;
关于 ModelAndView 的使用有 8 种方式:
方式一【最常用,推荐】:
@RequestMapping("/modelAndView1")
public ModelAndView modelAndView1(){
ModelAndView modelAndView = new ModelAndView();
User user = new User();
user.setId(10);
user.setName("张三");
modelAndView.setViewName("view");
modelAndView.addObject("user",user);
return modelAndView;
}
方式二:
@RequestMapping("/modelAndView2")
public ModelAndView modelAndView2(){
ModelAndView modelAndView = new ModelAndView();
User user = new User();
user.setId(10);
user.setName("张三");
modelAndView.addObject("user",user);
View view = new InternalResourceView("/view.jsp");
modelAndView.setView(view);
return modelAndView;
}
方式三:
@RequestMapping("/modelAndView3")
public ModelAndView modelAndView3(){
ModelAndView modelAndView = new ModelAndView("view");
User user = new User();
user.setId(10);
user.setName("张三");
modelAndView.addObject("user",user);
return modelAndView;
}
方式四:
@RequestMapping("/modelAndView4")
public ModelAndView modelAndView4(){
View view = new InternalResourceView("/view.jsp");
ModelAndView modelAndView = new ModelAndView(view);
User user = new User();
user.setId(10);
user.setName("张三");
modelAndView.addObject("user",user);
return modelAndView;
}
方式五:
@RequestMapping("/modelAndView5")
public ModelAndView modelAndView5(){
Map<String,Object> map = new HashMap<String,Object>();
User user = new User();
user.setId(10);
user.setName("张三");
map.put("user",user);
ModelAndView modelAndView = new ModelAndView("view",map);
return modelAndView;
}
方式六:
@RequestMapping("/modelAndView6")
public ModelAndView modelAndView6(){
Map<String,Object> map = new HashMap<String,Object>();
User user = new User();
user.setId(10);
user.setName("张三");
map.put("user",user);
View view = new InternalResourceView("/view.jsp");
ModelAndView modelAndView = new ModelAndView(view,map);
return modelAndView;
}
方式七:
@RequestMapping("/modelAndView7")
public ModelAndView modelAndView7(){
User user = new User();
user.setId(10);
user.setName("张三");
ModelAndView modelAndView = new ModelAndView("view","user",user);
return modelAndView;
}
方式八:
@RequestMapping("/modelAndView8")
public ModelAndView modelAndView8(){
User user = new User();
user.setId(10);
user.setName("张三");
View view = new InternalResourceView("/view.jsp");
ModelAndView modelAndView = new ModelAndView(view,"user",user);
return modelAndView;
}
4. @ModelAttribute
Spring MVC 还可以通过 @ModelAttribute 注解的方式添加模型数据,如何使用呢?
- 定义一个方法,该方法用来返回要填充到模型数据中的对象;
- 给该方法添加 @ModelAttribute 注解;
注意:该方法不是响应请求的业务方法。
@RequestMapping("/modelAttributeTest")
public String modelAttributeTest(){
return "view";
}
@ModelAttribute
public User getUser(){
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
添加 @ModelAttribute 注解的方法,会在 Spring MVC 调用任何一个业务方法之前被自动调用。
所以在执行 modelAttributeTest 业务方法之前,会首先调用 getUser 方法,获取返回值 user 对象,SpringMVC框架会自动将该对象填充到模型数据中,进而绑定到域对象中。
我们知道域对象中的数据都是以键值对(key - value)的形式保存的,那么此时的 key 是什么呢?默认取模型数据对应类的类名首字母小写,即:User 类首字母小写"user “,所以 JSP 页面中,可以直接通过” user "取值。
如果 getUser 没有放返回值,则必须手动在该方法中填充模型数据,使用 Map 或者 Model。
@ModelAttribute
public void getUser1(Map<String,Object> map){
User user = new User();
user.setId(1);
user.setName("张三");
map.put("user", user);
}
@RequestMapping("/modelAttributeTest")
public String modelAttributeTest(){
return "view";
}
@ModelAttribute
public void getUser2(Model model){
User user = new User();
user.setId(1);
user.setName("张三");
model.addAttribute("user", user);
}
@RequestMapping("/modelAttributeTest")
public String modelAttributeTest(){
return "view";
}
5. @SessionAttribute
上面介绍的方式都是将模型数据绑定到 request 对象中,如果需要将模型数据绑定 session 对象中,只需要在类定义处添加 @SessionAttribute(value="user")
注解即可。
@Controller
@SessionAttributes(value = "user")
public class DataHandler {
@RequestMapping("/modelAndView1")
public ModelAndView modelAndView1(){
ModelAndView modelAndView = new ModelAndView();
User user = new User();
user.setId(10);
user.setName("张三");
modelAndView.setViewName("view");
modelAndView.addObject("user",user);
return modelAndView;
}
}
此时,无论通过上述何种方式执行业务代码,将模型数据绑定到 request 对象中的同时,也会将模型数据绑定到 session 对象中,request 和 session 对象中同时存在模型数据。
@SessionAttributes 除了可以通过 key 值绑定,还可以通过模型数据的数据类型绑定。
@Controller
@SessionAttributes(types=User.class)
public class DataHandler {
//...
}
@SessionAttributes 也可以同时绑定多个模型数据
@Controller
@SessionAttributes(value={"user","address"})
public class DataHandler {
//...
}
@Controller
@SessionAttributes(types={User.class,Address.class})
public class DataHandler {
//...
}
实际项目开发中,尽管 Spring MVC框架提供了多种模型数据绑定的方式,但是使用最多和最常用的还是 ModelAndView,而后面两种通过注解完成数据绑定的模式,既不易理解,使用起来也容易出错,所以为了本文内容的完整性,这两种方式仅供了解。