Spring MVC 中创建的模型对象到底在哪里?
Posted
技术标签:
【中文标题】Spring MVC 中创建的模型对象到底在哪里?【英文标题】:Where exactly is a model object created in Spring MVC? 【发布时间】:2019-02-07 03:24:34 【问题描述】:在阅读了一些教程和从 docs.spring.org 参考阅读的初始文档后,我了解到它是在开发人员创建的 POJO 类的控制器中创建的。 但是在阅读本文时,我遇到了以下段落:
方法参数上的
@ModelAttribute
表示应从模型中检索该参数。如果模型中不存在,则应首先实例化参数,然后将其添加到模型中。一旦出现在模型中,参数的字段应该从具有匹配名称的所有请求参数中填充。这在 Spring MVC 中称为数据绑定,这是一种非常有用的机制,可以让您不必单独解析每个表单字段。@RequestMapping(value="/owners/ownerId/pets/petId/edit", method = RequestMethod.POST) public String processSubmit(@ModelAttribute Pet pet)
Spring Documentation
在该段中,最令人不安的是一行:
"如果模型中不存在..."
数据如何存在于模型中? (因为我们还没有创建模型 - 它将由我们创建。)
另外,我看到一些控制器方法接受Model
类型作为参数。那是什么意思?是否在某处创建了Model
?如果是这样,谁在为我们创造它?
【问题讨论】:
【参考方案1】:如果模型中不存在,则应先实例化参数,然后将其添加到模型中。
该段描述了以下一段代码:
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
实例,该实例记录 HandlerMethodArgumentResolver
s 和 HandlerMethodReturnValueHandler
s 在调用控制器方法的过程中做出的与模型和视图相关的决策。
新创建的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
。如我们所见,第二个方法接收到为第一个方法创建的实例(具有自己的隐藏值)。
【讨论】:
这真的很有趣。但是关于“没有找到任何东西来填充实例”:在什么情况下它会找到一些东西?不是总是需要创建一个新实例吗? @daniu,如果发生å重定向,ModelAndViewContainer
的新实例可能会由重定向初始化程序设置的从RequestContextUtils.getInputFlashMap(request)
提取的属性填充
@daniu Spring 不认为重定向足以改变游戏规则以清除它们的整个“上下文”。这是有道理的:在invokeInternal
中,如果发生重定向,我确实希望为editPet
创建的宠物发生。
哦,这就是为什么你甚至添加了internal()
一个,明白了。谢谢:)
@AndrewTobilko 您能否也回答这个问题“另外,我看到一些控制器方法接受模型类型作为参数。这是什么意思?它是在某处创建模型吗?如果那么是谁在为我们创造呢?”【参考方案2】:
为了回答这个问题,我在@andrew 回答的帮助下发现了一些代码 sn-ps。这证明了在为特定 URL 调用我们的控制器/处理程序之前创建了一个 ModelMap 实例[一个模型对象]
public class ModelAndViewContainer
private boolean ignoreDefaultModelOnRedirect = false;
@Nullable
private Object view;
private final ModelMap defaultModel = new BindingAwareModelMap();
....
.....
如果我们看到上面的 sn-p 代码(取自 spring-webmvc-5.0.8 jar)。 BindingAwareModelMap 模型对象早于创建。
为了更好地理解为类添加 cmets BindingAwareModelMap
/**
* Subclass of @link org.springframework.ui.ExtendedModelMap that automatically removes
* a @link org.springframework.validation.BindingResult object if the corresponding
* target attribute gets replaced through regular @link Map operations.
*
* <p>This is the class exposed to handler methods by Spring MVC, typically consumed through
* a declaration of the @link org.springframework.ui.Model interface. There is no need to
* build it within user code; a plain @link org.springframework.ui.ModelMap or even a just
* a regular @link Map with String keys will be good enough to return a user model.
*
@SuppressWarnings("serial")
public class BindingAwareModelMap extends ExtendedModelMap
....
....
【讨论】:
以上是关于Spring MVC 中创建的模型对象到底在哪里?的主要内容,如果未能解决你的问题,请参考以下文章
我在哪里可以找到可以在 Powershell 中创建的所有 COM 对象?