如果模型中不存在,则应先实例化参数,然后将其添加到模型中。
该段描述了以下一段代码:
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
} else {
// Create attribute instance
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
...
}
}
...
mavContainer.addAllAttributes(attribute);
(取自ModelAttributeMethodProcessor#resolveArgument)
对于每个请求,Spring 都会初始化一个 ModelAndViewContainer 实例,该实例记录 HandlerMethodArgumentResolvers 和 HandlerMethodReturnValueHandlers 在调用控制器方法的过程中做出的与模型和视图相关的决策。
新创建的ModelAndViewContainer 对象最初填充有flash attributes(如果有):
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
表示模型中已经存在的参数不会被初始化。
为了证明这一点,让我们来看一个实际的例子。
Pet 类:
public class Pet {
private String petId;
private String ownerId;
private String hiddenField;
public Pet() {
System.out.println("A new Pet instance was created!");
}
// setters and toString
}
PetController 类:
@RestController
public class PetController {
@GetMapping(value = "/internal")
public void invokeInternal(@ModelAttribute Pet pet) {
System.out.println(pet);
}
@PostMapping(value = "/owners/{ownerId}/pets/{petId}/edit")
public RedirectView editPet(@ModelAttribute Pet pet, RedirectAttributes attributes) {
System.out.println(pet);
pet.setHiddenField("XXX");
attributes.addFlashAttribute("pet", pet);
return new RedirectView("/internal");
}
}
让我们向 URI /owners/123/pets/456/edit 发出 POST 请求并查看结果:
A new Pet instance was created!
Pet[456,123,null]
Pet[456,123,XXX]
A new Pet instance was created!
Spring 创建了一个ModelAndViewContainer,但没有找到任何东西来填充实例(这是来自客户端的请求;没有任何重定向)。由于模型是空的,Spring 必须通过调用打印该行的默认构造函数来创建一个新的 Pet 对象。
Pet[456,123,null]
一旦出现在模型中,参数的字段应该从具有匹配名称的所有请求参数中填充。
我们打印了给定的Pet 以确保所有字段petId 和ownerId 都已正确绑定。
Pet[456,123,XXX]
我们设置hiddenField 来检查我们的理论并重定向到方法invokeInternal,它也需要@ModelAttribute。如我们所见,第二个方法接收到为第一个方法创建的实例(具有自己的隐藏值)。