目录

 

1、Spring MVC 提供了以下几种途径输出数据到模型中:

2、ModelAndView类

3、Map及Model

4、@SessionAttributes注解

5、@ModelAttribute注解


1、Spring MVC 提供了以下几种途径输出数据到模型中:

(1)ModelAndView: 处理方法返回值类型为 ModelAndView时, 方法体即可通过该对象添加模型数据

(2)Map 及 Model: 入参为org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。

(3)@SessionAttributes: 将模型中的某个属性暂存到HttpSession 中,以便多个请求之间可以共享这个属性

(4)@ModelAttribute: 方法入参标注该注解后, 入参的对象就会放到数据模型中

2、ModelAndView类

控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。

(1)添加模型数据方法

MoelAndView addObject(String attributeName, Object attributeValue)

ModelAndView addAllObject(Map<String, ?> modelMap– )

(2)设置视图方法:

void setView(View view)

void setViewName(String viewName)

(3)演示

前端

<body>
  <a href="springmvc/testModelAndView">Test ModelAndView</a>
</body>

服务端代码

package com.atguigu.springmvc.handlers

@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest{
  private static final String SUCCESS="success";

  
  @RequestMapping(value="/testModelAndView")
  public String ModelAndView testModelAndView(){
    String viewName=SUCCESS;
    ModelAndView modelAndView=new ModelAndView(viewName);

    //添加模型数据到ModelAndView中

   modelAndView.addObject("time",new Date());
   
    return modelAndView;
  }
}

说明:SpringMVC会把ModelAndView的model中的数据放入到request域对象

进入success.jsp(转到的页面也是MVC模型的V)。域对象就可以调用啦

<body>
  <h4>Success Page</h4>
  time:${requestScope.time}
</body>

3、Map及Model

SpringMVC在内部使用了一个org.springframework.ui.Model 接口存储模型数据。该方法主要用于添加Map类型数据进入模型,实际上也可以添加Model类型或ModeIMao类型参数。实际上形参是一个ExtendedModeMap实例对象,由图可见,该对象实现了Map接口,和Model接口,因此可以添加他们的类型参数,但是一般用于添加Map类型参数到请求域request对象中

 Model接口
作用:将值存放到request对象

具体步骤:

(1)SpringMVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。

(2)如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据

package com.atguigu.springmvc.handlers

@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest{
  private static final String SUCCESS="success";

  
  @RequestMapping(value="/testMap")
  public String testMap(Map<String,Object> map){
    map.put("names",Arrays.asList("Tom","Jerry","Mike"));
    return SUCCESS;
  }
}

前端

<body>
  <a href="springmvc/testMap">Test Map</a>
</body>

视图Jsp

<body>
  <h4>Success Page</h4>
  time:${requestScope.time}
  names:${requestScope.names}
</body>

4、@SessionAttributes注解

@SessionAttributes注解的作用是向服务器session添加键值对

该注解只能放在类的上面,不能放在方法上面。

如果不加@SessionAttributes注解,就会像前面一样加到请求域里面,加了@SessionAttributes注解才会变成session,就会同时放在请求域和session

用法:

@SessionAttributes(types=User.class) 会将隐含模型中所有类型为 User.class 的属性添加到会话(session)中。

@SessionAttributes(value={“user1”, “user2”})  会将键为user1,和user2 的属性值添加到会话中

@SessionAttributes(types={User.class, Dept.class})  会将User对象和 Dept对象 添加的会话中

@SessionAttributes(value={“user1”, “user2”}, types={Dept.class}) 会将键为user1 和 user2 以及 Dept类对象添加到会话中

演示:

package com.atguigu.springmvc.handlers

//除了键位user的属性值,所有String类型的属性值都会进入session
@SessionAttributes(value={"user"},types={String.class})
@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest{
  private static final String SUCCESS="success";

  
  @RequestMapping(value="/testSessionAttributes")
  public String testSessionAttributes(Map<String,Object> map){
    User user=new User("Tom","123456","tom@atguigu.com",15);//上面那个文章的user
    map.put("user",user);
    return SUCCESS;
  }
}

前端

<body>
  <a href="springmvc/testSessionAttributes">Test SessionAttributes</a>
</body>

Jsp页面

<body>
  <h4>Success Page</h4>
  time:${requestScope.time}
  names:${requestScope.names}
  session user:${sessionScope.user}
</body>

注意:关于异常

如果在处理类定义处标注了@SessionAttributes(“xxx”),则尝试从会话中获取该属性,并将其赋给该入参,然后再用请求消息填充该入参对象。如果在会话中找不到对应的属性,则抛出 HttpSessionRequiredException 异常

5、@ModelAttribute注解

5.1 @ModelAttribute注解详细介绍

引入问题:一条数据记录,三个字段要修改两个字段,有一个字段不能修改。

问题:如果按照常规操作,new一个新的对象作为数据记录,修改两个属性,另一个不变。但是这样的效果就是,修改了两个,不能修改的变成空了。

 

解决思路。从数据库获取该条记录的对象(是从数据库获取的,不是new的),这样这三个属性值都是数据库本身的值了。然后再修改,就可以达到效果啦

 

有@ModelAttribute标记的方法,会在每个目标方法执行之前被SpringMVC调用(先调用@ModelAttribute标记的方法,再调用目标方法)

代码演示

要求:模拟修改操作

1.原始数据为: 1, Tom, 123456,tom@atguigu.com,12 (实际不是数据库读取的,是自己手动添加的原始数据)

2.密码不能被修改.

3,表单回显,模拟操作直接在表单填写对应的属性值

创建User类,这个POJO对象可以自动获取完表单内容

package com.atguigu.springmvc.entities;

public class User{
  private Integer id;
  private String username;
  private String password;

  private String email;
  private int age;
  

  //空参构造器,有参构造器,get方法,set方法,toString方法
}

前端首页 index.jsp

<body>
  <form action="springmvc/testModelAttribute" method="post">
    <!--一般id值都是隐藏的-->
    <input type="hidden" name="id" value="1">
    username<input type="text" name="username" value="Tom">
    email:<input type="text" name="email" value="tom@atguigu.com">
    age:<input type="text" name="age" value="12">
    <input type="submit" value="Submit">
  </form>

</body>

服务端

package com.atguigu.springmvc.handlers


@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest{
  private static final String SUCCESS="success";

  @ModelAttribute
  public void getUser(@RequestParam(value="id",required=false) Integer id,Map<String,Object> map){
    if(id != null){
      //模拟从数据库中获取对象,当然实际是自己手动输入的
      User user=new User(1,"Tom","123456","tom@atguigu.com",12);
      System.out.println("从数据库中获取一个对象:"+user);
      map.put("user",user);
    }    
  }
  
  @RequestMapping(value="/testModelAttribute")
  public String testModelAttribute(User user){
    System.out.println("修改"+user);
    return SUCCESS;
  }
}

运行流程说明:

① 执行@ModelAttribute注解修饰的方法:从数据库中取出对象,把对象放入到了Map中.键为: user

② SpringMVC从Map中取出User对象,并把表单的请求参数赋给该User对象的对应属性.

③ SpringMVC把上述对象传入目标方法的参数.

注意:在@ModelAttribute修饰的方法中,放入到Map时的键需要和目标方法入参类型的第一个字母小写的字符串一致!

5.2 源码分析流程

step1 调用@ModelAttribute注解修饰的方法,实际上把@ModelAttribute方法中Map中的数据放在了implicitModel中.

step2 解析请求处理器的目标参数,实际上该目标参数来自于@WebDateBinder对象的target属性

(1)创建WebDataBinder对象

I 确定objectName属性:若传入的attrName属性值为"",则objectName为类名第一个字母小写。

注意:attrName. 若目标方法的POJO属性使用了@ModelAttribute来修饰,则attrName值即为@ModelAttribute的value属性值

II 确定target属性:

① 在implicitModel中查找attrName对应的属性值.若存在, ok

② 若不存在:则验证当前Handler是否使用了@SessionAttributes进行修饰,若使用了,则尝试从Session中获取attrName所对应的属性值.若session中没有对应的属性值,则抛出了异常.

③ 若Handler没有使用@SessionAttributes进行修饰,或@SessionAttributes中没有使用value值指定的key和attrName相匹配,则通过反射创建了POJO对象

(2) SpringMVC把表单的请求参数赋给了WebDataBinder的target对应的属性。

(3) *SpringMVC会把WebDataBinder的attrName和target给到implicitModel,进而传到request域对象中

(4)把WebDataBinder的target作为参数传递给目标方法的入参.

进一步简化整理以上过程:

SpringMVC确定目标方法POJO类型入参的过程:

(1)确定一个key:

1),若目标方法的POJ0类型的参数没有使用@ModelAttribute作为修饰,则key为POJO类名第一个字母的小写

2) 若使用了@ModelAttribute来修饰,则key为@ModelAttribute注解的value属性值

(2)在implicitModel中查找key对应的对象,若存在,则作为入参传入

1),若在@ModelAttribute标记的方法中在Map中保存过,且key和(1)确定的key一致,则会获职到.

(3)若implicitModel中不存在key对应的对象,则检查当前的Handler是否使用@SessionAttributes注解修饰,若使用了该注解,且@SessionAttributes注解的value属性值中包含了key,则会从HttpSession中来获取key所对应的value值,若存在则直接传入到目标方法的入参中,若不存在则将抛出异常.

(4)若Handler没有标识@SessionAttributes注解或@SessionAttributes注解的value值中不包含key,则会通过反射来创建POJO类型的参数,传入为目标方法的参数

(5)SpringMVC会把key和POJO类型的对象保存到implicitModel中,进而会保存到request中

5.3 @ModelAttribute注解修饰入参

上一节已经讲过了全套,这里专门讲下修饰入参

总之常用的是,入参用了@ModelAttribute注解修饰,就可以自己取键的名字了,如果没用该注解修饰,默认键名为POJO类名第一个字母的小写。入参不用POJO类名第一个字母的小写命名,同时又没用@ModelAttribute注解修饰,并自己取名,springmvc就找不到。

(1)有@ModelAttribute标记的方法,会在每个目标方法执行之前被SpringMVC调用!

(2)@ModelAttribute注解也可以来修饰目标方法POJO类型的入参,其value属性值有如下的作用:

  1). SpringMvC会使用value属性值在implicitModel中查找对应的对象,若存在则会直接传入到目标方法的入参中.

  2). SpringMvC会使用value为key, POJO类型的对象为value,存入到request中.

 

 

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐