上一篇已经说了三种处理模型数据的方法, 最后一种是使用@ModelAttribute注解,在一开始学习这个注解的时候感觉很懵,完全不知道它是怎么起作用的,后来自己实践了一下才搞懂,感觉这个注解真是信息量太大了!!
@ModelAttribute可以在方法上使用,也可以在方法中的参数中使用,关于在这两个不同的地方使用时是有很大的差别的:
- 在方法上使用时, SpringMVC在调用所有的请求处理器(被@RequestMapping注解的方法)前会执行被这个注解标识的所有方法;
- 在方法的参数上使用时
一、 方法上使用@ModelAttribute
使用在方法上的目的就是用来添加模型数据,由于被@ModelAttribute注解的方法要在所有被@RequestMapping注解的方法调用前调用,所以可以将该被该注解标识的方法看成是请求拦截器,在所有请求处理器之前执行,一个Controller类中可以有多个被@ModelAttribute注解的方法,由于作为拦截器,所以它接收的参数类型和被@RequestMapping注解的方法的参数类型相同,即亦可以使用像@RequestParam和@PathVariable等注解。另外,在方法上使用该注解时有两种方式:
- 方法没有返回值:当没有返回值时,我们就需要向该注解标识的方法中传递一个Model或者Map类型的参数来作为存储模型数据的容器,其实这里的容器类型可以使用在上一篇中说到的Map的对应的子类型也是可以的,实例如下:
@ModelAttribute
public void getGlobalInfo(Model model) {
model.addAttribute("area", "China");
}
也可以写成这样:
@ModelAttribute
public void getGlobalInfo(Map<String, Object> map) {
map.put("area", "China");
}
这样该Controller类中所有的@RequestMapping注解标识方法的返回的视图中都可以通过EL表达式获取这个模型数据;
- 方法有返回值:当有返回值时,模型数据是被存储到SpringMVC为我们创建的容器中,实例如下:
@ModelAttribute
public Person getPerson(){
Person person = new Person();
person.setName("lmy86263");
person.setAge(24);
return person;
}
Person是一个POJO类,如下:
public class Person implements Serializable{
private static final long serialVersionUID = 2158697392661485138L;
private String name;
private Integer age;
<span style="white-space:pre"> </span>// setter getter
}
在这里就是将Person对象存储到SpringMVC为我们创建的隐式容器中,属性的名称是”person“,至于这个属性名是怎么来的?不过不加指定,默认的属性名是非限定类型名称的第一个字母小写后作为属性名,这里就是将Person首字母小写作为属性名。如果不想使用默认的则
可以使用@ModelAttribute注解中的value属性来指定属性名,比如使用@ModelAttribute("p"),这是在前台页面只能使用${p.name}而不能使用${person.name}。
还有一个疑问,网上一直没人写过,我在这里试一下,上面可以看到返回的诗句都是POJO类型,如果要是普通类型的数据是什么样的?尤其是普通类型使用默认参数名时是怎么样的?真的是String类型参数,属性名就是"string"?如下:
@ModelAttribute
public Integer getString(){
// String msg = "string type";
// return msg;
Integer number = 12345;
return number;
}
经过测试,果然是这样,即使是String或者其基本类型的包装类也是安装上述所说的方式进行命名的,这里要注意的是不要使用基本类型,比如int,double等当返回值,这会导致抛出javax.el.ELException: The identifier [int] is not a valid Java identifier as required by section 1.19 of the EL specification这种异常,是EL表达式不能解析这种类型。
@ModelAttribute还可以应用到多个Controller类中,通过建立一个被@ControllerAdvice注解(这个注解很有用,在进行异常处理时还会用到这个注解)的类,在该类中建立一个被@ModelAttribute的方法,这时在该方法中填入的模型数据可以在多个被声明的Controller类中使用,如果@ControllerAdvice没有指定特定的Controller,那么该方法会应用到所有的Controller中,可以看下面的链接。
更进一步,@ModelAttribute注解还可以使用在@RequestMapping注解标识的方法中,这个时候
该请求处理器的返回值就不是一个视图名,而是被认为是一个模型数据。但是我觉得这样使用不太好,所以尽量不要这样使用。
二、 参数上使用@ModelAttribute
在方法参数上使用@ModelAttribute是为了从模型中获取数据来传递到该参数上。如果模型中没有该对应的属性名,那么该类型的参数首先会实例化然后在加入到模型中,关于该类型的实例来自哪里是一个比较复杂的问题,如果有兴趣可以参考SpringMVC的文档。;如果发现有对应的属性名,那么参数的字段就会被该模型值填充,就是所谓的数据绑定。其实在从模型中取数据之前,将该注解使用在@RequestMapping方法参数上可以获取表单或者json数据,当表单或者ajax传递过来值时首先获取这部分的值,如果对应的对象的字段不存在才会去模型中查找对应的数据,好乱啊,看下面的实例:
有一个@ModelAttribute方法如下:
@ModelAttribute
public User getPerson(){
User user = new User();
user.setName("lmy");
user.setAge(20);
Address address = new Address();
address.setCity("ts");
address.setProvince("hb");
user.setAddress(address);
System.out.println("here user " + user);
return user;
}
此处的User和上一篇中的User相同不再指出。
@RequestMapping方法如下:
@RequestMapping("/testModelAttributeParam")
public String testModelAttributeParam(@ModelAttribute("user") User user){
System.out.println(user);
return "view";
}
前台页面如下,没有传递city:
<html>
<body>
<form action="testModelAttributeParam" method="post">
<input type="text" name="name">name</input>
<input type="text" name="age">age</input>
<input type="text" name="address.province">province</input>
<!-- <input type="text" name="address.city">city</input> -->
<input type="submit" value="submit" />
</form>
</body>
</html>
后台显示结果如下:
可以看出只有在前台没有传递city数据时,才会到模型中查找对应User中的city数据填充到@RequestMapping方法的参数中。
相关文章: