Где именно находится модельный объект, созданный в Spring MVC? - PullRequest
0 голосов
/ 01 сентября 2018

Пройдя несколько уроков и начальное чтение документа из ссылки на 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 ]

0 голосов
/ 03 сентября 2018

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

Если в модели отсутствует аргумент, сначала необходимо создать аргумент, а затем добавить его в модель.

В этом параграфе описывается следующий фрагмент кода:

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. Как мы видим, второй метод получил экземпляр (со своим скрытым значением), который был создан для первого метода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...