为了更形象地引出 @ModelAttribute注解:
下面来仔细看这两幅图:
需求:对数据库中的表单信息进行修改(例如表单中有三项数据)
现在只需更新其中的两项数据,第三项数据不变(即与数据库中的信息保持一致)
1:通过new一个新对象实现更新操作:
结果:第三项数据为null;
2.从数据库中取出数据,在原来的基础上进行更新操作:
结果:完美的实现了需求;
下面来看代码:
表单信息:
<!-- 模拟和修改操作
1.原始数据:1 , Tom , 12 ,tom@qq.com
2.密码不能被修改。
3.表单回显,模拟操作在表单填写对应的属性值。
-->
<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="tom@qq.com"/>
<br>
age:<input type="text" name="age" value="12"/>
<br>
<input type="submit" value="Submit"/>
</form>
首先是上图1方式进行的更新:
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("修改 :" + user);
return SUCCESS;
}
提交更改操作:
结果返回:
(注意看password的值!!虽然age变化了,但密码仍为null)
修改 :User [id=1, username=Tom, password=null, email=tom@qq.com, age=15]
其次是上图2方式进行的更新:
在上述代码的基础上增加一个用@ModelAttribute注解标注的类
/*
* 有 @ModelAttribute 标记的方法 ,会在每个目标方法执行之前被Spring MVC调用!
* @ModelAttribute 注解也可以来修饰目标方法POJO类型的入参,其中value属性值有如下作用:
* 1).Spring MVC会使用value属性值在implicitModel中查找对应的对象,若存在则会直接传入到目标方法的入参中。
* 2).Spring MVC会以value为key值,POJO类型的对象为value,存入到request中。
*/
@ModelAttribute
public void getUser(@RequestParam(value="id",required = false) Integer id,
Map<String, Object> map) {
System.out.println("ModelAttribute method" );
if( id!=null) {
//模拟从数据库中获取对象
User user = new User(1, "Tom", "123456" , "tom@qq.com", 12);
System.out.println("从数据库中获取一个对象 :" + user);
map.put("user", user);
}
}
结果返回:
(注意看前后的对象中password值!!以及age也成功更新!)
ModelAttribute method
从数据库中获取一个对象 :User [id=1, username=Tom, password=123456, email=tom@qq.com, age=12]
修改 :User [id=1, username=Tom, password=123456, email=tom@qq.com, age=15]
@ModelAttribute总结:
/*
* 运行流程:
* 1.执行 @ModelAttribute 注解修饰的方法:从数据库中取出对象,把对象放入到了Map中,键为:user;
* 2.SpringMVC 从 Map中取出User对象,并把表单中请求参数赋给该User对象的对应属性;
* 3.Spring MVC把上述对象传入目标方法的参数;
*/
注意:在@ModelAttribute 注解修饰的方法中,放入到Map时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
/*
* 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源码分析:
* 1.调用@ModelAttribute注解修饰的方法。实际上把@ModelAttribute方法中Map中的数据放在了implicitModel中。
* 2.解析请求处理器的目标参数,实际上该目标参数来自于WebDataBinder对象的target属性
*
* 1). 创建 WebDataBinder 对象
*
* 1.确定objectName属性:
* 若传入的attrName属性值为"",则objectName为类名第一个字母小写。
* *注意:attrName: 若目标方法的POJO属性使用了@ModelAttribute来修饰,则attrName值为@ModelAttribute的value属性值
* 例如:public String testModelAttribute(@ModelAttribute("user") User user) {}
*
* 2.确定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 作为参数传递给目标方法的入参;
*/