Где именно находится модельный объект, созданный в Spring MVC?

Пройдя несколько уроков и начальное чтение документа из ссылки на docs.spring.org, я понял, что он создан в контроллере класса POJO, созданного разработчиком. Но, читая это, я наткнулся на абзац ниже:

@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 инициализирует 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!

Весна создала ModelAndViewContainer и не нашел ничего, чтобы заполнить экземпляр (это запрос от клиента; перенаправлений не было). Поскольку модель пуста, Spring пришлось создать новый Pet объект, вызывая конструктор по умолчанию, который напечатал строку.

Pet[456,123,null]

При наличии в модели поля аргумента должны быть заполнены всеми параметрами запроса, имеющими совпадающие имена.

Мы напечатали данное Pet чтобы убедиться, что все поля petId а также ownerId был связан правильно.

Pet[456,123,XXX]

Мы установили hiddenField проверить нашу теорию и перенаправить на метод invokeInternal который также ожидает @ModelAttribute, Как видим, второй метод получил экземпляр (со своим скрытым значением), который был создан для первого метода.

Чтобы ответить на вопрос, я нашел несколько фрагментов кода с помощью ответа @andrew. Что оправдывает экземпляр 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 {
      ....
      ....
     }
Другие вопросы по тегам