一:说明
1.使用场景
在下面的场景中:
使用new对象时,最后的效果是两个被赋值了,但是还有一个值是空值的,这个不符合只更新两个值,另一个值不变的情况。
正确的做法:
三个值得数据不是new出来的,而是从数据库中获取。
这种做法适合于本文的注解。
2.注解的知识点
有@ModelAttribute标记的方法,会在每个目标方法执行之前被SpringMVC调用。
二:程序
1.bean对象--Person
1 package com.spring.bean; 2 3 public class Person { 4 private Integer id; 5 private String username; 6 private String password; 7 private String email; 8 private int age; 9 private Address address; 10 11 public Person() {} 12 public Person(String username, String password, String email, int agem,Address address) { 13 super(); 14 this.username = username; 15 this.password = password; 16 this.email = email; 17 this.age = age; 18 this.address = address; 19 } 20 //不带address的构造函数 21 public Person(String username, String password, String email, int age) { 22 super(); 23 this.username = username; 24 this.password = password; 25 this.email = email; 26 this.age = age; 27 } 28 //带id的构造函数 29 public Person(Integer id,String username, String password, String email, int age) { 30 super(); 31 this.id=id; 32 this.username = username; 33 this.password = password; 34 this.email = email; 35 this.age = age; 36 } 37 public String getUsername() { 38 return username; 39 } 40 public void setUsername(String username) { 41 this.username = username; 42 } 43 public String getPassword() { 44 return password; 45 } 46 public void setPassword(String password) { 47 this.password = password; 48 } 49 public String getEmail() { 50 return email; 51 } 52 public void setEmail(String email) { 53 this.email = email; 54 } 55 public int getAge() { 56 return age; 57 } 58 public void setAge(int age) { 59 this.age = age; 60 } 61 public Address getAddress() { 62 return address; 63 } 64 public void setAddress(Address address) { 65 this.address = address; 66 } 67 public Integer getId() { 68 return id; 69 } 70 public void setId(Integer id) { 71 this.id = id; 72 } 73 @Override 74 public String toString() { 75 return "Person [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + ", age=" 76 + age + "]"; 77 } 78 79 80 }
2.insex
1 <%@ page language="java" contentType="text/html; charset=utf-8" 2 pageEncoding="utf-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 7 <title>Insert title here</title> 8 </head> 9 <body> 10 <!-- 11 原始数据:1,tom,123456,tom@123.com,12 12 要求:数据不能改变 13 表单回显 id username password email age 14 --> 15 <form action="helloworld9" method="post"> 16 <input type="hidden" name="id" value="1"/><br/><br> 17 username:<input type="text" name="username" value="tom"/><br/><br> 18 email:<input type="text" name="email" value="tom@123.com"/><br/><br> 19 age:<input type="text" name="age" value="12"/><br/><br> 20 <input type="submit" value="Submit"> 21 </form> 22 </body> 23 </html>
3.controller
1 package com.spring.it; 2 3 import java.util.Map; 4 5 import org.springframework.stereotype.Controller; 6 import org.springframework.web.bind.annotation.ModelAttribute; 7 import org.springframework.web.bind.annotation.RequestMapping; 8 import org.springframework.web.bind.annotation.RequestParam; 9 10 import com.spring.bean.Person; 11 12 @Controller 13 public class ModelAttributeControl { 14 15 /** 16 * 这是表单的post请求 17 */ 18 @RequestMapping("/helloworld9") 19 public String hello(Person person) { 20 System.out.println("修改:"+person); 21 return "success"; 22 } 23 24 /** 25 * 模拟从数据库中取得一个Person对象 26 */ 27 @ModelAttribute 28 public void getPerson(@RequestParam(value="id",required=false) Integer id,Map<String,Object> map) { 29 if(id!=null) { 30 //原始数据:1,tom,123456,tom@123.com,12 31 Person person=new Person(1, "tom", "123456", "tom@123.com", 12); 32 System.out.println("从数据库中获取一个数据:"+person); 33 map.put("person", person); 34 } 35 } 36 }
4.效果
其他的数据保持不变,主要看的数据是psaaword:
三:会导致的异常
1.异常
通过源码的分析:
在确定targer时,在implicitModel中查找attrName对象的属性值,若存在,则ok
如果不存在,则验证当前是否使用了@SessionAttributes进行修饰,进行了修饰,则尝试从Session中获取值,若session中没有attrName对应的属性值,则跑出异常。
若没有使用@SessionAttributes进行修饰,或者@SessionAttributes没有使用键与attrName匹配,则通过反射创建POJO对象。
2.SpringMVC确定目标方法POJO入参
1.确定一个 key:
1). 若目标方法的 POJO 类型的参数木有使用 @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 中.
四:ModelAttribute注解修饰POJO类型的入参
1.程序实例
1 @ModelAttribute 2 public void getUser(@RequestParam(value="id",required=false) Integer id, 3 Map<String, Object> map){ 4 System.out.println("modelAttribute method"); 5 if(id != null){ 6 //模拟从数据库中获取对象 7 User user = new User(1, "Tom", "123456", "tom@atguigu.com", 12); 8 System.out.println("从数据库中获取一个对象: " + user); 9 10 map.put("abc", user); 11 } 12 } 13 14 @RequestMapping("/testModelAttribute") 15 public String testModelAttribute(@ModelAttribute("abc") User user){ 16 System.out.println("修改: " + user); 17 return SUCCESS; 18 }
2.解释
上面的put中放入的key是abc,所以需要在使用user的地方进行ModelAttribute注解。
如果是默认的情况下,就是说不使用注解,就需要使用默认的key,将第一个字母小写后的类名。