SpringMVC深入二

来源:互联网 发布:php做商城程序流程图 编辑:程序博客网 时间:2024/06/06 03:34

SpringMVC深入二

处理模型数据

MVC设计模式是,发一个请求到目标处理器,目标处理器去调业务方法,业务方法可能有返回值,可能是一个对象或者集合。然后转发页面,需要把业务对象返回值(对象或者集合)在页面上显示出来。

在SpringMVC中用以下几种途径输出模型数据:

  • ModelAndView:处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据
  • Map及Model:入参为org.springframework.ui.Model,org.springframework.ui.ModelMapjava.util.Map时,处理方法返回时,Map中的数据会自动添加到模型中
  • @SessionAttributes:将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性
  • @ModelAttribute:方法入参标注该注解后,入参的对象就会放到数据模型中

ModelAndView

SpringMVC会把ModelAndView的Model中数据放入到request域对象中。

如下,返回视图为SUCCESS,添加一个time数据

/** * 目标方法的返回值可以是ModelAndView类型。 * 其中可以包含视图和视图模型信息 * @return */@RequestMapping("/testModelAndView")public ModelAndView testModelAndView(){    String viweName = SUCCESS;    ModelAndView modelAndView = new ModelAndView(viweName);    //添加模型数据到ModelAndView中    modelAndView.addObject("time", new Date());    return modelAndView;}

所以在success.jsp页面中可以取出time对应的数据。

time:${requestScope.time}

Map及Model

目标方法的入参可以加入一个Map或者Model的参数。
SpringMVC在内部使用了一个org.springframework.ui.Model 接口存储模型数据。
具体步骤:

  • SpringMVC在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器
  • 如果方法的入参为Map或Model类型,SpringMVC会将隐含模型的引用传递给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。

如下:

/** * 目标方法可以添加Map类型的参数,还可传入ModelMap或Model,通常传入Map类型 * @param map * @return */@RequestMapping("/testMap")public String testMap(Map<String, Object> map) {    map.put("names", Arrays.asList("tom", "jerry", "mike"));    return SUCCESS;}

success.jsp页面中可以取出names对应的数据

names:${requestScope.names}

@SessionAttributes

若希望多个请求共用某个模型属性数据,则可以在控制器上类上标注一个@SessionAttributes,SpringMVC将在模型中对应的属性暂存到HttpSession中。

@SessionAttributes除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪个模型属性需要放到会话中

  • @SessionAttributes(types=User.class)会将隐含模型中所有的类型为User.class的属性添加到会话中
  • @SessionAttributes(value={"user1","user2"})
  • @SessionAttributes(value={User.class, Dept.class})
  • @SessionAttributes(value={"user1","user2"}, types={Dept.class})

如下:

@RequestMapping("/testSessionAttributes")public String testSessionAttributes(Map<String, Object> map) {    User user = new User("wz", "hello");    map.put("user", user);    map.put("school", "wuhan");    return SUCCESS;}

mapuserschool放入到了request的请求域中。
当在控制器上加上@SessionAttributes(value={"user"},types={String.class})后,可以把userschool放入到session中。
user使用的是value属性值,school使用的是types 属性值。
需要注意的是这个注解只能放在类上。

在jsp页面中可以取出值:

user:${requestScope.user}<br>user:${sessionScope.user}<br>school:${requestScope.school}<br>school:${sessionScope.school}

@ModelAttribute

通过例子来说明,假设有如下的表单,其原始数据为id=1,username=tom,password=123456,email=123@123.com,age=12,在做修改的时候,不能在表单中显示密码,所以密码是不知道的,现在把age修改为13

<form action="springmvc/testModelAttribute" method="post">    <input type="hidden" name="id" value="1" />    username:<input type="text" name="username" value="tom"/>    <br>    email:<input type="text" name="email" value="123@123.com"/>    <br>    age:<input type="text" name="age" value="12"/>    <br>    <input type="submit" value="submit" /></form>

映射代码如下:

@RequestMapping("/testModelAttribute")public String testModelAttribute(User user){    System.out.println("修改:" + user);    return SUCCESS;}

控制台输出如下:

修改:User [id=1, username=tom, password=null, email=123@123.com, age=13]

可以发现passwordnull,这样在修改时就会有问题,需要先获取到password的值。

然后,添加如下的代码,观察结果。
这里模拟从数据库中获取数据,把user放入到map中,并使用了@ModelAttribute注解:

@ModelAttributepublic void getUser(@RequestParam(value="id", required=false) Integer id,        Map<String, Object> map){    if (id != null) {        //模拟从数据库中获取对象        User user= new User(1, "tom", "123456", "123@123.com", 12);        System.out.println("从数据库中获取一个对象:" + user);        map.put("user", user);    }}

提交表单,控制台输出结果如下:

从数据库中获取一个对象:User [id=1, username=tom, password=123456, email=123@123.com, age=12]修改:User [id=1, username=tom, password=123456, email=123@123.com, age=13]

可以发现表单的userpassword现在有了值。
在改之前先查数据库,然后把表单的属性值赋给从数据库获取那个对象的对应属性

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

运行流程:

1.执行@ModelAttribute 修饰的方法:从数据库中取出对象,把对象放入到Map中,键为:user
2.SpringMVC从Map中取出User对象,并把表单的请求参数赋给该User对象的对应属性。
3.SpringMVC把上述对象传入目标方法的参数

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

源码分析的流程:

  1. 调用@ModelAttribute注解修饰的方法,实际上把@ModelAttribute方法中Map中的数据放在了implicitModel中。
  2. 解析请求处理器的目标参数,实际上该目标参数来自于WebDataBinder对象的target属性。

    1. 创建WebDataBinder对象

      1. 确定objectName属性:若传入的attrName属性值为”“,则objectName为类名第一个字母小写。注意:attrName若目标方法的POJO使用了@ModelAttribute来修饰,则attrName值即为@ModelAttribute的value属性值。
      2. 确定target属性:在implicitModel中查找对应的属性,若存在,则返回。若不存在,则验证当前Handler是否使用了@SessionAttributes进行修饰,若使用了,则尝试从session中获取attrName所对应的属性值。若session中没有对应的属性值,则抛出异常。若Handler没有使用@SessionAttributes进行修饰,或@SessionAttributes中没有value值指定的key和attrName相匹配,则通过反射创建了POJO对象。
    2. SpringMVC把表单的请求参数赋给了WebDataBindertarget对应的属性。

    3. SpringMVC会把WebDataBinderattrNametarget给到implicitModel,进而传到request域
    4. WebDataBinder的target作为参数传递给目标方法的入参。

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

  1. 确定一个key:若目标方法的POJO类型的参数没有使用@ModelAttribute作为修饰,则key为POJO类名第一个字母的小写。若使用@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中。

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

  1. SpringMVC会使用value属性值在implicitModel中查找对应的对象,若存在则会直接传入到目标方法的入参中
  2. SpringMVC会以value作为key,POJO类型的对象为value,存到request中。

对与上面的例子,把原来map中key由user改为abc,在对应的返回页面中查看request域中的值:

@ModelAttributepublic void getUser(@RequestParam(value="id", required=false) Integer id,        Map<String, Object> map){    if (id != null) {        //模拟从数据库中获取对象        User user= new User(1, "tom", "123456", "123@123.com", 12);        System.out.println("从数据库中获取一个对象:" + user);        map.put("abc", user);    }}@RequestMapping("/testModelAttribute")public String testModelAttribute(User user){    System.out.println("修改:" + user);    return SUCCESS;}

success.jsp页面中取出值:

<br>abc:${requestScope.abc}<br>user:${requestScope.user}

在页面上显示结果如下,abc对应的是数据库中的值,user对应的是表单中的值:

abc:User [id=1, username=tom, password=123456, email=123@123.com, age=12] user:User [id=1, username=tom, password=null, email=123@123.com, age=15]

如果,我们将handler的入参POJO对象,使用@ModelAttribute修饰:

@RequestMapping("/testModelAttribute")public String testModelAttribute(@ModelAttribute("abc") User user){    System.out.println("修改:" + user);    return SUCCESS;}

结果如下,abc为更新后的对象,user为空:

abc:User [id=1, username=tom, password=123456, email=123@123.com, age=15] user:

@SessionAttributes注解引发的异常

移除如下使用@ModelAttribute注解的代码:

@ModelAttributepublic void getUser(@RequestParam(value="id", required=false) Integer id,        Map<String, Object> map){    if (id != null) {        //模拟从数据库中获取对象        User user= new User(1, "tom", "123456", "123@123.com", 12);        System.out.println("从数据库中获取一个对象:" + user);        map.put("user", user);    }}

只保留

@RequestMapping("/testModelAttribute")public String testModelAttribute(@ModelAttribute("user") User user){    System.out.println("修改:" + user);    return SUCCESS;}

则抛出异常:org.springframework.web.HttpSessionRequiredException: Session attribute 'user' required - not found in session

原因是,会先在implicitModel中查找user对应的属性,若存在,则返回。若不存在,则验证当前Handler是否使用了@SessionAttributes进行修饰,若使用了,则尝试从session中获取attrName所对应的属性值。若session中没有对应的属性值,则抛出异常。

0 0
原创粉丝点击