@ModelAttribute 注解
试想一个场景,如果我们在修改某个对象的时候,首先该对象是从数据库取出来,展示到前端,但是我们都知道网页有编辑按钮,能够修改个人信息。但是呢,一部分不能修改,比如用户的账号是不能被轻易修改的,而部分属性(密码)是能被修改的。那么在前端的表单提交中,会在服务器接受到一个 new 出一个新的对象,比如 Admin,那么这个时候,我们的不想被修改的属性,账号 account 就为空了呀?
那么如何避免某些属性不被更改,如账号,但又能够修改密码呢?
- 使用隐藏表单控件
- 使用禁止编辑的样式
但是上面的操作在浏览器,使用 F12 的时候,就会被其他懂行的人恶意修改。并且如果被修改的字段很多,隐藏的表单控件也就不实用了。如何解决呢?
SpringMVC 提供的解决方案是要修改的对象不是 new 出来的,而是从数据库中来,并且需要修改的字段只针对从数据库中来的对象进行修改。需要在方法上使用 @ModelAttribute 注解,但是这个方法并不是普通的请求处理器的目标方法,也就是说这个方法没有 @RequestMapping 注解。而且该注解注解的方法,会在其他所有的目标方法执行之前,都会执行被 @ModelAttribute 注解的方法执行一次。
@ModelAttribute
public void testModelAttribute(Map map,String account) {
AdminDaoImpl adminDaoImpl = new AdminDaoImpl();
// 从数据库取出对象
Admin admin = adminDaoImpl.getAdminByAccount(account);
//放入对象
map.put("admin",admin);
return ;
}
@RequestMapping("testModelAttribute")
public String testSetAdmin(Admin admin) {
System.out.println(admin);
return "success";
}
那么当 表单提交到 testModelAttribute 地址的时候,里面的 account 账号就不为 null,而是有值的。
-
细节
-
请求处理器目标方法的入参,默认会通过类名首字母小写的名称在 @ModelAttribute 注解的方法中,添加到请求域的数据中去寻找。
-
所以如果把 map.put 的 key 改成 ad 等其他的值,那么 testSetAdmin 的入参 admin 就是重新 new 出来的值。
-
解决方法:使用 @ModelAttribute注解指定查找的属性模型名称。
@ModelAttribute public void testModelAttribute(Map map,String account) { AdminDaoImpl adminDaoImpl = new AdminDaoImpl(); // 从数据库取出对象 Admin admin = adminDaoImpl.getAdminByAccount(account); //放入对象 map.put("ad",admin); return ; } @RequestMapping("testModelAttribute") public String testSetAdmin(@ModelAttribute("ad") Admin admin) { System.out.println(admin); return "success.jsp"; }
-
运行流程
- 执行 @ModelAttribute 注解的方法,从数据库取出对象,把对象放到 Map 中。键为 admin
- SpringMVC 从 Map 取出 admin 对象。把表单的请求参数赋值给键 admin 对应的对象属性
- SpringMVC 把上述对象传入目标方法的参数
- 注意:在 @ModelAttribute 修饰的方法中,放入 Map 时的键需要和目标方法入参类型,的第一个字母小写的字符串一致。
确定目标方法 POJO 类型的入参过程
-
确定 map 中的 key
- 若目标方法的 POJO 类型没有使用 @ModelAttribute 修饰,则 key 为 POJO 的类名(第一个字母小写)
- 若是使用,则 key 为 @ModelAttribute 注解的 value 属性值
-
在 implicitModel 中查找 key 对于的对象,
- 存在则作为入参传入,如果在 @ModelAttribute 注解的方法保存过,且 key 和 在第一步 确定 map 中的 key 一致,则获取到
-
若不存在 implicitModel ,则检查请求解析器(Handler)是否使用了 @SessionAttribute 注解注释
- 若使用,且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所对应的 value 值,存入到目标方法的入参中,不存在则报错
- 没有使用 @SessionAttribute 注解 ,则使用反射创建 POJO 类型参数,传入目标方法
-
SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中