Если в модели отсутствует аргумент, сначала необходимо создать аргумент, а затем добавить его в модель.
В этом параграфе описывается следующий фрагмент кода:
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
с и HandlerMethodReturnValueHandler
s во время вызова метода контроллера.
Недавно созданный ModelAndViewContainer
объект изначально заполняется флэш-атрибутами (если есть):
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");
}
}
Давайте сделаем POST-запрос к URI /owners/123/pets/456/edit
и посмотрим результаты:
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
. Как мы видим, второй метод получил экземпляр (со своим скрытым значением), который был создан для первого метода.