Где именно создается объект модели в Spring MVC?
после прохождения некоторых учебных пособий и первоначального чтения документа из docs.spring.org ссылка я понял, что он создан в контроллере класса POJO, созданного разработчиком. Но, читая это, я наткнулся на следующий абзац:--5-->
An
@ModelAttribute
в аргументе метода указывается, что аргумент должен быть извлечен из модели. Если аргумент отсутствует в модели, его следует сначала создать, а затем добавить в модель. Когда-то присутствует в модель, поля аргумента должны быть заполнены из всех параметров запроса, которые имеют совпадающие имена. Это известно как привязка данных в Spring MVC, очень полезный механизм, который избавляет вас от необходимости анализировать каждое поле формы отдельно.@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) public String processSubmit(@ModelAttribute Pet pet) { }
в абзаце больше всего тревожит строка:
"если нет в модели ... "
как данные могут быть там в модели? (Потому что мы не создали модель - она будет создана нами.)
кроме того, я видел несколько методов контроллера, принимая Model
тип в качестве аргумента. Что это значит? Он получает Model
создал где-то? Если да, то кто создает его для нас?
2 ответов
если в модели нет аргумента, его следует сначала создать, а затем добавить в модель.
в абзаце описывается следующий фрагмент кода:
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 инициализирует a ModelAndViewContainer
экземпляр, который записывает модель и связанные с представлением решения, принятые HandlerMethodArgumentResolver
s и HandlerMethodReturnValueHandler
s во время вызова метода контроллера.
новая ModelAndViewContainer
"объект" изначально заполняется flash атрибуты (если есть):
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!
Весна создала ModelAndViewContainer
и не нашел ничего, чтобы заполнить экземпляр (это запрос от клиента; не было никаких редиректов). Поскольку модель пуста, Spring пришлось создать новый Pet
объект, вызывая конструктор по умолчанию, который напечатал строку.
Pet[456,123,null]
раз присутствующие в модели поля аргумента должны быть заполнены из всех параметров запроса, имеющих совпадающие имена.
мы напечатали данного Pet
убедитесь, что все поля!--19--> и ownerId
были правильно привязаны.
Pet[456,123,XXX]
мы hiddenField
проверить нашу теорию и перенаправить на метод invokeInternal
, который также ожидает @ModelAttribute
. Как мы видим, второй метод получил экземпляр (с собственным скрытым значением), который был создано для первого метода.
чтобы ответить на вопрос, я нашел несколько фрагментов кода с помощью @andrew answer. Что оправдывает экземпляр ModelMap[объект модели] создается задолго до того, как наш контроллер/обработчик вызывается для определенного URL
public class ModelAndViewContainer {
private boolean ignoreDefaultModelOnRedirect = false;
@Nullable
private Object view;
private final ModelMap defaultModel = new BindingAwareModelMap();
....
.....
}
Если мы видим приведенный выше код фрагмента (взятый из spring-webmvc-5.0.8 jar). BindingAwareModelMap объект модели создается задолго до этого.
для лучшего понимания добавление комментариев для класса 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 {
....
....
}