ModelAndView

Posted 好多个码农

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ModelAndView相关的知识,希望对你有一定的参考价值。

探寻 Spring MVC 视图层的实现机制

前言

Spring MVC 框架作为一个实现 MVC 设计模式的框架,很重要的一项工作是在控制器获取业务数据并返回给客户端,即在 JSP 页面展示业务数据,使用的技术是通过 EL 表达式从域对象中取值。

在 Servlet 中,我们可以直接调用 Web 资源给域对象传值,在 Spring MVC 框架中,如何完成这个操作?这一讲我们就来学习 Spring MVC 框架的业务数据绑定。

首先来理解这句话,业务数据的绑定是指将业务数据绑定给 JSP 域对象,首先回顾一下域对象都有哪些。

JSP 四大作用域对应的四个内置对象分别是:pageContext、request、session 和 application。

业务数据的绑定是由 ViewResolver 来完成的,开发时,我们先添加业务数据,再交给 ViewResolver 来绑定,因此学习的重点在于如何添加业务数据,Spring MVC 提供了以下几种方式添加业务数据:

  • Map

  • Model

  • ModelAndView

  • @SessionAttributes

  • @ModelAttribute

开发中经常用到的域对象是 request 和 session,我们就针对这两个域对象进行讲解,pageContext 和 application 可以通过获取原生 Servlet 资源的方式进行绑定,实际开发中使用不多。

业务数据绑定到 request 域对象

Map

Spring MVC 在内部使用 Model 接口存储业务数据,在调用业务方法前会创建一个隐含对象作为业务数据的存储容器。设置业务方法的入参为 Map 类型,Spring MVC 会将隐含对象的引用传递给入参。开发者可以对模型中的所有数据进行管理,包括访问和添加。我们只需要在业务方法添加 Map 类型的入参,方法体中便可通过对入参的操作来完成业务数据的添加。

@RequestMapping("/mapTest")
public String mapTest(Map<String,Object> map){
  User user = new User();
  user.setId(1);
  user.setName("张三");
  map.put("user", user);
  return "index";
}


业务方法完成,返回业务数据和视图信息给 DispatcherServlet,DispatcherServlet 通过 ViewResolver 对视图信息进行解析,逻辑视图映射到物理视图,同时将业务数据绑定到 JSP 的 request 域对象中,在 JSP 页面可直接通过 EL 表达式取值。

<body>
    ${user.name }
</body>


启动 Tomcat,运行,结果如下图所示。

Model

Model 与 Map 类似,业务方法通过入参来完成业务数据的绑定。

@RequestMapping("/modelTest")
public String modelTest(Model model){
    User user = new User();
    user.setId(1);
    user.setName("张三");
    model.addAttribute("user", user);
    return "index";
}


ModelAndView

与 Map 或者 Model 不同的是,ModelAndView 不但包含业务数据,同时也包含了视图信息,如果使用 ModelAndView 来处理业务数据,业务方法的返回值必须是 ModelAndView 对象。

业务方法中对 ModelAndView 进行两个操作:填充业务数据、绑定视图信息。

关于 ModelAndView 的使用有 8 种方式,具体操作如下所示。

@RequestMapping("/modelAndViewTest1")
public ModelAndView modelAndViewTest1(){
    ModelAndView modelAndView = new ModelAndView();
    User user = new User();
    user.setId(1);
    user.setName("张三");
    modelAndView.addObject("user", user);
    modelAndView.setViewName("index");
    return modelAndView;
}
​
@RequestMapping("/modelAndViewTest2")
public ModelAndView modelAndViewTest2(){
    ModelAndView modelAndView = new ModelAndView();
    User user = new User();
    user.setId(1);
    user.setName("张三");
    modelAndView.addObject("user", user);
    View view = new InternalResourceView("/index.jsp");
    modelAndView.setView(view);
    return modelAndView;
}
​
@RequestMapping("/modelAndViewTest3")
public ModelAndView modelAndViewTest3(){
    ModelAndView modelAndView = new ModelAndView("index");
    User user = new User();
    user.setId(1);
    user.setName("张三");
    modelAndView.addObject("user", user);
    return modelAndView;
}
​
@RequestMapping("/modelAndViewTest4")
public ModelAndView modelAndViewTest4(){
    View view = new InternalResourceView("/index.jsp");
    ModelAndView modelAndView = new ModelAndView(view);
    User user = new User();
    user.setId(1);
    user.setName("张三");
    modelAndView.addObject("user", user);
    return modelAndView;
}
​
@RequestMapping("/modelAndViewTest5")
public ModelAndView modelAndViewTest5(){
    Map<String,Object> map = new HashMap<String,Object>();
    User user = new User();
    user.setId(1);
    user.setName("张三");
    map.put("user", user);
    ModelAndView modelAndView = new ModelAndView("index", map);
    return modelAndView;
}
​
@RequestMapping("/modelAndViewTest6")
public ModelAndView modelAndViewTest6(){
    Map<String,Object> map = new HashMap<String,Object>();
    User user = new User();
    user.setId(1);
    user.setName("张三");
    map.put("user", user);
    View view = new InternalResourceView("/index.jsp");
    ModelAndView modelAndView = new ModelAndView(view, map);
    return modelAndView;
}
​
@RequestMapping("/modelAndViewTest7")
public ModelAndView modelAndViewTest7(){
    User user = new User();
    user.setId(1);
    user.setName("张三");
    ModelAndView modelAndView = new ModelAndView("index", "user", user);
    return modelAndView;
}
​
@RequestMapping("/modelAndViewTest8")
public ModelAndView modelAndViewTest8(){
    User user = new User();
    user.setId(1);
    user.setName("张三");
    View view = new InternalResourceView("/index.jsp");
    ModelAndView modelAndView = new ModelAndView(view, "user", user);
    return modelAndView;
}

HttpServletRequest

Spring MVC 可以在业务方法直接获取到 Servlet 原生 Web 资源,只需在方法定义时添加 HttpServletRequest 入参即可,在方法体中可直接对 request 对象进行操作,如下所示。

@RequestMapping("requestTest")
public String requestTest(HttpServletRequest request){
    User user = new User();
    user.setId(1);
    user.setName("张三");
    request.setAttribute("user", user);
    return "index";
}


@ModelAttribute

Spring MVC 还可以通过 @ModelAttribute 注解的方式添加业务数据,具体使用有如下两个步骤:

  • 定义一个方法,该方法用来返回要填充到业务数据中的对象;

  • 给该方法添加 @ModelAttribute 注解,注意,该方法并不是响应请求的业务方法。

@RequestMapping("/modelAttributeTest")
public String modelAttributeTest(){
    return "index";
}

@ModelAttribute
public User getUser(){
    User user = new User();
    user.setId(1);
    user.setName("张三");
    return user;
}


添加 @ModelAttribute 注解的方法,会在 Spring MVC 调用任何一个业务方法之前自动调用。因此在执行 modelAttributeTest 业务方法之前,会首先调用 getUser 方法,获取返回值 user 对象,Spring MVC 会自动将该对象填充到业务数据中,进而绑定到域对象中。

我们知道域对象中的数据都是以键值对 (key-value) 的形式保存的,那么此时的 key 是什么呢?默认取业务数据对应类的首字母小写之后的类名,如 User 类首字母小写之后为 "user",因此 JSP 页面中,可以直接通过 "user" 取值。

若 getUser 没有返回值,则必须手动在该方法中填充业务数据,使用 Map 或者 Model 均可。

@ModelAttribute
public void getUser2(Map<String,Object> map){
    User user = new User();
    user.setId(1);
    user.setName("张三");
    map.put("user", user);
}

@ModelAttribute
public void getUser3(Model model){
    User user = new User();
    user.setId(1);
    user.setName("张三");
    model.addAttribute("user", user);
}


业务数据绑定到 session 域对象

上述方式全部是将业务数据绑定到 request 对象中,如果需要将业务数据绑定到 session 对象中,只需要在类定义处添加 @SessionAttributes(value="user") 注解即可,如下所示。

@Controller
@SessionAttributes(value="user")
public class HelloHandler {
//省略代码
}


此时,无论通过上述哪种方式来执行业务代码,将业务数据绑定到 request 对象中的同时,也会将业务数据绑定到 session 对象中,也就是说 request 和 session 对象会同时存在业务数据。

@SessionAttributes 除了可以通过 key 值绑定,也可以通过业务数据的数据类型进行绑定,如下所示。

@Controller
@SessionAttributes(types=User.class)
public class HelloHandler {
//省略代码
}


@SessionAttributes 可同时绑定多个业务数据,如下所示。

@Controller
@SessionAttributes(value={"user","address"})
public class HelloHandler {
//省略代码
}
@Controller
@SessionAttributes(types={User.class,Address.class})
public class HelloHandler {
//省略代码
}


总结

本节课我们讲解了 Spring MVC 的业务数据解析,具体是指将控制器的业务方法处理结果响应给客户端的过程,即 C(Controller)—— V(View)的映射,可以使用 Spring MVC 自带的 ModelAndView 组件同时将业务数据和视图信息进行绑定,这种方式是我们开发中常用的,同时也可以使用 Model、Map 来完成业务数据的解析。

以上是关于ModelAndView的主要内容,如果未能解决你的问题,请参考以下文章

如何使用modelandview进行重定向

Spring boot - ModelAndView 不调用另一个页面

SpringMVC在JSP页面取不到ModelAndView中的值

用ModelAndView返回视图结果返回的是对应RequestMapping拼接的路径

如何在 spring 控制器部分更改 modelandview 的视图内容?

SpringMVC ModelAndView 不跳转