对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

@ModelAttribute

您可以使用 @ModelAttribute 注解在方法参数上访问模型中的属性,或者在不存在时实例化它。模型属性还会被与字段名称匹配的 HTTP Servlet 请求参数值覆盖。这被称为数据绑定,它可以让你免于处理解析和转换单个查询参数和表单字段。以下示例展示了如何实现这一点:spring-doc.cadn.net.cn

@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 实例是通过以下方式获取的:spring-doc.cadn.net.cn

另一种不使用 @ModelAttribute 方法 来提供它,或依赖框架来创建模型属性的方法是,使用一个 Converter<String, T> 来提供实例。当模型属性名称与请求值(如路径变量或请求参数)的名称匹配,并且存在从 String 到模型属性类型的 Converter 时,就会应用此方法。在下面的例子中,模型属性名称是 account,这与 URI 路径变量 account 匹配,并且已注册了一个 Converter<String, Account>,该方法可以从数据存储中加载 Accountspring-doc.cadn.net.cn

@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上的字段名称进行匹配。在应用类型转换后,匹配的字段将被填充。有关数据绑定(和验证)的更多信息,请参见验证。有关自定义数据绑定的更多信息,请参见DataBinderspring-doc.cadn.net.cn

数据绑定可能会导致错误。默认情况下,会抛出一个BindException。但是,要在控制器方法中检查此类错误,您可以在@ModelAttribute的旁边立即添加一个BindingResult参数,如下例所示:spring-doc.cadn.net.cn

@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),如下例所示:spring-doc.cadn.net.cn

@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 ValidationSpring 验证)。以下示例展示了如何操作:spring-doc.cadn.net.cn

@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 注解。spring-doc.cadn.net.cn

在使用GraalVM编译为原生镜像时,上文描述的隐式@ModelAttribute支持无法正确实现相关数据绑定的反射提示的提前推断。因此,建议在GraalVM原生镜像使用场景中,显式标注方法参数为@ModelAttribute