Spring MVC @ModelAttribute详解
如果配置了一个可以将String类型的账户值转换成Account类型实例的转换器Converter,那么上面这段代码就可以工作的很好,而不需要再额外写一个@ModelAttribute方法。@ModelAttribute方法通常被用来填充一些公共需要的属性或数据,比如一个下拉列表所预设的几种状态,或者宠物的几种类型,或者去取得一个HTML表单渲染所需要的命令对象,比如Account等。@Model
方法使用@ModelAttribute标注
@ModelAttribute标注可被应用在方法或方法参数上。
标注在方法上的@ModelAttribute说明方法是用于添加一个或多个属性到model上。这样的方法能接受与@RequestMapping标注相同的参数类型,只不过不能直接被映射到具体的请求上。
在同一个控制器中,标注了@ModelAttribute的方法实际上会在@RequestMapping方法之前被调用。
以下是示例:
// Add one attribute
// The return value of the method is added to the model under the name "account"
// You can customize the name via @ModelAttribute("myAccount")
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountManager.findAccount(number);
}
// Add multiple attributes
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountManager.findAccount(number));
// add more ...
}
@ModelAttribute方法通常被用来填充一些公共需要的属性或数据,比如一个下拉列表所预设的几种状态,或者宠物的几种类型,或者去取得一个HTML表单渲染所需要的命令对象,比如Account等。
@ModelAttribute标注方法有两种风格:
- 在第一种写法中,方法通过返回值的方式默认地将添加一个属性;
- 在第二种写法中,方法接收一个Model对象,然后可以向其中添加任意数量的属性。
可以在根据需要,在两种风格中选择合适的一种。
一个控制器可以拥有多个@ModelAttribute方法。同个控制器内的所有这些方法,都会在@RequestMapping方法之前被调用。
@ModelAttribute方法也可以定义在@ControllerAdvice标注的类中,并且这些@ModelAttribute可以同时对许多控制器生效。
属性名没有被显式指定的时候又当如何呢?在这种情况下,框架将根据属性的类型给予一个默认名称。举个例子,若方法返回一个Account类型的对象,则默认的属性名为"account"。可以通过设置@ModelAttribute标注的值来改变默认值。当向Model中直接添加属性时,请使用合适的重载方法addAttribute(..)-即带或不带属性名的方法。
@ModelAttribute标注也可以被用在@RequestMapping方法上。这种情况下,@RequestMapping方法的返回值将会被解释为model的一个属性,而非一个视图名,此时视图名将以视图命名约定来方式来确定。
方法参数使用@ModelAttribute标注
@ModelAttribute标注既可以被用在方法上,也可以被用在方法参数上。
标注在方法参数上的@ModelAttribute说明了该方法参数的值将由model中取得。如果model中找不到,那么该参数会先被实例化,然后被添加到model中。在model中存在以后,请求中所有名称匹配的参数都会填充到该参数中。
这在Spring MVC中被称为数据绑定,一个非常有用的特性,我们不用每次都手动从表格数据中转换这些字段数据。
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute Pet pet) { }
以上面的代码为例,这个Pet类型的实例可能来自哪里呢?有几种可能:
- 它可能因为@SessionAttributes标注的使用已经存在于model中
- 它可能因为在同个控制器中使用了@ModelAttribute方法已经存在于model中——正如上一小节所叙述的
- 它可能是由URI模板变量和类型转换中取得的(下面会详细讲解)
- 它可能是调用了自身的默认构造器被实例化出来的
@ModelAttribute方法常用于从数据库中取一个属性值,该值可能通过@SessionAttributes标注在请求中间传递。在一些情况下,使用URI模板变量和类型转换的方式来取得一个属性是更方便的方式。这里有个例子:
@RequestMapping(path = "/accounts/{account}", method = RequestMethod.PUT)
public String save(@ModelAttribute("account") Account account) {
}
这个例子中,model属性的名称("account")与URI模板变量的名称相匹配。如果配置了一个可以将String类型的账户值转换成Account类型实例的转换器Converter<String, Account>,那么上面这段代码就可以工作的很好,而不需要再额外写一个@ModelAttribute方法。
下一步就是数据的绑定。WebDataBinder类能将请求参数——包括字符串的查询参数和表单字段等——通过名称匹配到model的属性上。成功匹配的字段在需要的时候会进行一次类型转换(从String类型到目标字段的类型),然后被填充到model对应的属性中。
进行了数据绑定后,则可能会出现一些错误,比如没有提供必须的字段、类型转换过程的错误等。若想检查这些错误,可以在标注了@ModelAttribute的参数紧跟着声明一个BindingResult参数:
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
拿到BindingResult参数后,可以检查是否有错误,可以通过Spring的<errors>表单标签来在同一个表单上显示错误信息。
BindingResult被用于记录数据绑定过程的错误,因此除了数据绑定外,还可以把该对象传给自己定制的验证器来调用验证。这使得数据绑定过程和验证过程出现的错误可以被搜集到一起,然后一并返回给用户:
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
// ...
}
又或者可以通过添加一个JSR-303规范的@Valid标注,这样验证器会自动被调用。
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
更多推荐
所有评论(0)