|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
@ModelAttribute
您可以使用 @ModelAttribute 注解在方法参数上访问模型中的属性,或者在不存在时实例化它。模型属性还会被与字段名称匹配的 HTTP Servlet 请求参数值覆盖。这被称为数据绑定,它可以让你免于处理解析和转换单个查询参数和表单字段。以下示例展示了如何实现这一点:
-
Java
-
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { (1)
// method logic...
}
| 1 | 绑定 Pet 的一个实例。 |
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { (1)
// method logic...
}
| 1 | 绑定 Pet 的一个实例。 |
上面的 Pet 实例是通过以下方式获取的:
-
从模型中检索,它可能已被添加到其中通过一个 @ModelAttribute 方法。
-
如果模型属性在类级别
@SessionAttributes注解中列出,则从HTTP会话中获取。 -
通过
Converter获取,其中模型属性名称与请求值的名称匹配,例如路径变量或请求参数(参见下一个示例)。 -
使用其默认构造函数实例化。
-
通过与Servlet请求参数匹配的“主构造函数”实例化。参数名称通过JavaBeans
@ConstructorProperties或字节码中保留的运行时参数名称确定。
另一种不使用 @ModelAttribute 方法 来提供它,或依赖框架来创建模型属性的方法是,使用一个 Converter<String, T> 来提供实例。当模型属性名称与请求值(如路径变量或请求参数)的名称匹配,并且存在从 String 到模型属性类型的 Converter 时,就会应用此方法。在下面的例子中,模型属性名称是 account,这与 URI 路径变量 account 匹配,并且已注册了一个 Converter<String, Account>,该方法可以从数据存储中加载 Account:
-
Java
-
Kotlin
@PutMapping("/accounts/{account}")
public String save(@ModelAttribute("account") Account account) { (1)
// ...
}
| 1 | 使用显式属性名绑定一个Account的实例。 |
@PutMapping("/accounts/{account}")
fun save(@ModelAttribute("account") account: Account): String { (1)
// ...
}
| 1 | 使用显式属性名绑定一个Account的实例。 |
在获取模型属性实例后,应用数据绑定。WebDataBinder类将Servlet请求参数名称(查询参数和表单字段)与目标Object上的字段名称进行匹配。在应用类型转换后,匹配的字段将被填充。有关数据绑定(和验证)的更多信息,请参见验证。有关自定义数据绑定的更多信息,请参见DataBinder。
数据绑定可能会导致错误。默认情况下,会抛出一个BindException。但是,要在控制器方法中检查此类错误,您可以在@ModelAttribute的旁边立即添加一个BindingResult参数,如下例所示:
-
Java
-
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { (1)
if (result.hasErrors()) {
return "petForm";
}
// ...
}
| 1 | 在 @ModelAttribute 旁边添加一个 BindingResult。 |
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { (1)
if (result.hasErrors()) {
return "petForm"
}
// ...
}
| 1 | 在 @ModelAttribute 旁边添加一个 BindingResult。 |
在某些情况下,你可能需要访问没有数据绑定的模型属性。对于这种情况,你可以将Model注入到控制器中并直接访问它,或者,设置@ModelAttribute(binding=false),如下例所示:
-
Java
-
Kotlin
@ModelAttribute
public AccountForm setUpForm() {
return new AccountForm();
}
@ModelAttribute
public Account findAccount(@PathVariable String accountId) {
return accountRepository.findOne(accountId);
}
@PostMapping("update")
public String update(@Valid AccountForm form, BindingResult result,
@ModelAttribute(binding=false) Account account) { (1)
// ...
}
| 1 | 设置 @ModelAttribute(binding=false). |
@ModelAttribute
fun setUpForm(): AccountForm {
return AccountForm()
}
@ModelAttribute
fun findAccount(@PathVariable accountId: String): Account {
return accountRepository.findOne(accountId)
}
@PostMapping("update")
fun update(@Valid form: AccountForm, result: BindingResult,
@ModelAttribute(binding = false) account: Account): String { (1)
// ...
}
| 1 | 设置 @ModelAttribute(binding=false). |
您可以在数据绑定后自动应用验证,方法是添加jakarta.validation.Valid注解或Spring的@Validated注解(Bean Validation和Spring 验证)。以下示例展示了如何操作:
-
Java
-
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { (1)
if (result.hasErrors()) {
return "petForm";
}
// ...
}
| 1 | 验证 Pet 实例。 |
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { (1)
if (result.hasErrors()) {
return "petForm"
}
// ...
}
| 1 | 验证 Pet 实例。 |
请注意,使用 @ModelAttribute 是可选的(例如,设置其属性)。
默认情况下,任何不是简单值类型(由 BeanUtils#isSimpleProperty 确定)
且未被任何其他参数解析器解析的参数将被视为带有 @ModelAttribute 注解。
在使用GraalVM编译为原生镜像时,上文描述的隐式@ModelAttribute支持无法正确实现相关数据绑定的反射提示的提前推断。因此,建议在GraalVM原生镜像使用场景中,显式标注方法参数为@ModelAttribute。 |