1.@ModelAttribute注释void返回值的方法
@Controller
public class HelloModelController {
@ModelAttribute
public void populateModel(@RequestParam String abc, Model model) {
model.addAttribute("attributeName", abc);
}
@RequestMapping(value = "/helloWorld")
public String helloWorld() {
return "helloWorld.jsp";
}
}
在这个代码中,访问控制器方法helloWorld时,会首先调用populateModel方法,将页面参数abc(/helloWorld.ht?abc=text)放到model的attributeName属性中,在视图中可以直接访问。
jsp页面页面如下:
[html]
<c:out value="${attributeName}"></c:out>
2.@ModelAttribute注释返回具体类的方法
@Controller
public class Hello2ModelController {
@ModelAttribute
public User populateModel() {
User user=new User();
user.setAccount("ray");
return user;
}
@RequestMapping(value = "/helloWorld2")
public String helloWorld() {
return "helloWorld.jsp";
}
}
@Controller
public class Hello2ModelController {
@ModelAttribute
public User populateModel() {
User user=new User();
user.setAccount("ray");
return user;
}
@RequestMapping(value = "/helloWorld2")
public String helloWorld() {
return "helloWorld.jsp";
}
}
当用户请求 http://localhost:8080/test/helloWorld2.html时,首先访问populateModel方法,返回User对象,model属性的名称没有指定,它由返回类型隐含表示,如这个方法返回User类型,那么这个model属性的名称是user。
这个例子中model属性名称有返回对象类型隐含表示,model属性对象就是方法的返回值。它无须要特定的参数。
jsp 中如下访问:
[html] view plaincopyprint?
<c:out value="${user.account}"></c:out>
<c:out value="${user.account}"></c:out>
也可以指定属性名称
[java] view plaincopyprint?
@Controller
public class Hello2ModelController {
@ModelAttribute(value="myUser")
public User populateModel() {
User user=new User();
user.setAccount("ray");
return user;
}
@RequestMapping(value = "/helloWorld2")
public String helloWorld(Model map) {
return "helloWorld.jsp";
}
}
@Controller
public class Hello2ModelController {
@ModelAttribute(value="myUser")
public User populateModel() {
User user=new User();
user.setAccount("ray");
return user;
}
@RequestMapping(value = "/helloWorld2")
public String helloWorld(Model map) {
return "helloWorld.jsp";
}
}
jsp中如下访问:
[html] view plaincopyprint?
<c:out value="${myUser.account}"></c:out>
<c:out value="${myUser.account}"></c:out>
对象合并:
[java] view plaincopyprint?
@Controller
@Controller
public class Hello2ModelController {
@ModelAttribute
public User populateModel() {
User user=new User();
user.setAccount("ray");
return user;
}
@RequestMapping(value = "/helloWorld2")
public String helloWorld(User user) {
user.setName("老王");
return "helloWorld.jsp";
}
}
对象合并指定对象名称:
[java]
@Controller
public class Hello2ModelController {
@ModelAttribute("myUser")
public User populateModel() {
User user=new User();
user.setAccount("ray");
return user;
}
@RequestMapping(value = "/helloWorld2")
public String helloWorld(@ModelAttribute("myUser") User user) {
user.setName("老王");
return "helloWorld.jsp";
}
}
这样在jsp中可以使用如下方式访问
[html]
<c:out value="${myUser.name}"></c:out>
<c:out value="${myUser.account}"></c:out>
<c:out value="${myUser.name}"></c:out>
<c:out value="${myUser.account}"></c:out>
3.通过此特性控制权限.
我们可以在基类方法中控制写此注解,需要控制权限的控制器,继承控制器就可以了。
public class BaseController {
@ModelAttribute
public void populateModel() throws Exception {
SysUser user=ContextUtil.getCurrentUser();
if(user.getAccount().equals("admin")){
throw new Exception("没有权限");
}
}
}
需要控制权限的类继承BaseController
@Controller
public class Hello2ModelController extends BaseController {
@RequestMapping(value = "/helloWorld2")
public String helloWorld(@ModelAttribute("myUser") User user) {
user.setName("老王");
return "helloWorld.jsp";
}
}
这样就可以控制权限了,当然控制权限的方法有很多,比如通过过滤器等。这里只是提供一种思路。
需求:
要求修改数据库中的某条记录的部分,部分值不能给修改.例如:数据库表有3个字段,有1个字段不能被修改,而更新的时候,表单提交的只有两个参数,实体中只有两个属性被赋值,其中一个属性是null值.用平常的update更新,会有一个字段被置空.一般的更新方法如下图所示:
传统解决方案:
一.隐藏域但是隐藏域有两个问题,问题一:如果是比较敏感的字段,不能放隐藏域,另一个问题就是字段较多的情况,使用隐藏域比较麻烦.
二.更新之前,先做查询操作.把表单未提交的值通过查询获取.这样的方法也比较麻烦.
高质量解决方案:即使用SpringMVC注解@ModelAttribute
对象从数据库中获取的.如图所示:
出现这样的原因是对象是new出来的,建立一个新的对象,这个对象之是空的.
具体代码:
Hander:
- /**
- * 有 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用!
- */
- @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@atguigu.com", 12);
- System.out.println("从数据库中获取一个对象: " + user);
- map.put("user", user);
- }
- }
- @RequestMapping("/testModelAttribute")
- public String testModelAttribute(User user){
- System.out.println("修改: " + user);
- return SUCCESS;
- }
View:
- <!--
- 模拟修改操作
- 1. 原始数据为: 1, Tom, 123456,tom@atguigu.com,12
- 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@atguigu.com"/>
- <br>
- age: <input type="text" name="age" value="12"/>
- <br>
- <input type="submit" value="Submit"/>
- </form>
执行结果:
运行流程:
1.执行 @ModelAttribute注解修饰的方法:从数据库中取出对象,把对象放入到了 Map中.键为: user
2. SpringMVC从 Map中取出 User对象,并把表单的请求参数赋给该User对象的对应属性.
3. SpringMVC把上述对象传入目标方法的参数.
注意:在 @ModelAttribute修饰的方法中,放入到 Map时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
下面在dug的模式下,通过分析源代码来分析流程:
1.调用 @ModelAttribute注解修饰的方法.实际上把@ModelAttribute方法中 Map中的数据放在了implicitModel中.
2.解析请求处理器的目标参数,实际上该目标参数来自于WebDataBinder对象的 target属性
1).创建 WebDataBinder对象:
①.确定 objectName属性:若传入的 attrName属性值为"",则 objectName为类名第一个字母小写.
注意:attrName. 若目标方法的 POJO属性使用了@ModelAttribute来修饰,则 attrName值即@ModelAttribute
的 value属性值
②.确定 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作为参数传递给目标方法的入参.
pojo类型的参数:
(一)如何确定
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中.
(二)修饰入差
2. @ModelAttribute 注解也可以来修饰目标方法 POJO类型的入参,其 value属性值有如下的作用:
1). SpringMVC会使用 value属性值在 implicitModel中查找对应的对象,若存在则会直接传入到目标方法的入参中.
2). SpringMVC会一 value为 key, POJO类型的对象为 value,存入到 request中.
巧妙的使用@ModelAttribute属性解决更新有的值被置空的问题.学会站在巨人的肩膀上,提高自己的代码质量.