程序员社区

19. Spring MVC 模型数据绑定

Spring MVC 模型数据绑定

Spring MVC 框架可以快速地搭建起一个基于 MVC 的 Web 应用程序,但 JavaWeb 的底层仍然是基于 Servlet 的形式。

SpringMVC 与传统 Servlet 的比较:

  1. Spring MVC 首先解决的问题是 Servlet 业务方法的局限性;

    每个 Servlet 都要实现 HttpServlet 抽象类,才能具备接收和处理 http 的请求的功能;每个 Servlet 只有一个 doGet 和 doPost 方法。如果客户端向服务器发送了 10 个 GET 类型的请求,我们要么写 10 个 Servlet 去处理请求,要么在 1 个 Servlet 的 1 个 doGet 方法中完成 10 种不同的处理逻辑。

  2. Spring MVC 将 Servlet 的功能做了调整,Servlet 只负责请求的接收与分发(DispatcherServlet),而不负责具体业务实现,将请求分发给不同的 Handler,不需要实现任何接口,完全可以根据业务逻辑来自己定义。

SpringMVC 很重要的一项工作是在控制器获取模型数据并返回给客户端,在 JSP 页面展示模型数据,使用的技术是通过 EL 表达式从域对象中取值。

在 Servlet 中我们可以直接调用 web 资源给域对象传值,模型数据的绑定,是指将模型数据绑定给 JSP 域对象,JSP 四大作用域对应的四大内置对象分别是:pageContext、request、session、application

模型数据的绑定是由 ViewResolver 来完成的,开发时,我们先添加模型数据,再将绑定的工作交由ViewResolver 视图解析器来处理。

Spring MVC 提供了多种方式添加模型数据:

  1. Map
  2. Model
  3. ModelAndView
  4. @ModelAttribute
  5. @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}&emsp;用户名:${requestScope.user.name}
    <hr/>
    欢迎您!学号:${sessionScope.user.id}&emsp;用户名:${sessionScope.user.name}
    <hr/>
    欢迎您!学号:${applicationScope.user.id}&emsp;用户名:${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}&emsp;用户名:${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 进行两个操作:

  1. 填充模型数据;
  2. 绑定视图信息;

关于 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 注解的方式添加模型数据,如何使用呢?

  1. 定义一个方法,该方法用来返回要填充到模型数据中的对象;
  2. 给该方法添加 @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,而后面两种通过注解完成数据绑定的模式,既不易理解,使用起来也容易出错,所以为了本文内容的完整性,这两种方式仅供了解。

赞(0) 打赏
未经允许不得转载:IDEA激活码 » 19. Spring MVC 模型数据绑定

一个分享Java & Python知识的社区