Подтвердить что ты не робот

Форма представить в Spring MVC 3 - объяснение

У меня возникли проблемы с пониманием того, как форма представления в Spring 3 MVC работает.

Что я хочу сделать, это создать контроллер, который будет использовать имя пользователя и отобразить его для него. И как-то я это сделал, но я действительно не понимаю, как это работает. Так..

У меня есть форма, которая выглядит так:

<form:form method="post" modelAttribute="person">
    <form:label path="firstName">First name</form:label>
    <form:input path="firstName" />
    <br />

    <form:label path="lastName">Last name</form:label>
    <form:input path="lastName" />
    <br />

    <input type="submit" value="Submit" />
</form:form>

У меня также есть контроллер, который выглядит так:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String showHelloPage(Model model) {
        model.addAttribute("person", new Person());
        return "home";
    }

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public String sayHello(Person person, Model model) {
        model.addAttribute("person", person);
        return "home";
    }
}

Чтобы отобразить приветственное сообщение пользователю, я использую следующий код на странице JSP:

<c:if test="${not empty person.firstName and not empty person.lastName}">
    Hello ${person.firstName} ${person.lastName}!
</c:if>

И он работает (я опускаю файлы конфигурации XML, потому что они не имеют отношения к проблеме).

Я думал, что атрибут "modelAttribute" в форме указывает на переменную bean, которая должна быть заполнена значениями входов (как указано в их атрибутах "путь" ). Но выглядит, работает совсем по-другому. Если я удалю строку

model.addAttribute("person", new Person());

из метода showHelloPage. Я получаю (общее) исключение "Ни BindingResult, ни...".

Кроме того, в начале метод sayTHello выглядел так:

(...)
public String sayHello(@ModelAttribute("person") Person person, Model model) {
(...)

Я имею в виду, что у него была аннотация "ModelAttribute". Я добавил это, потому что в учебниках, которые я прочитал, он всегда присутствовал. Но после того, как я удалил его, все прошло хорошо, как и раньше.

Итак, мой вопрос - в чем заключается использование аннатации "ModelAttribute"? Можно ли опустить атрибут "modelAttribute" в форме? И вторая часть, каков способ (возможно, аннотация) для создания формы, автоматически связывает значения входов с соответствующими свойствами bean (которые будут объявлены как параметр метода)? Без необходимости добавления пустой bean перед отправкой формы (как я должен сделать это сейчас).

Спасибо за ваши ответы (которые не являются ссылками на документацию Spring, потому что я ее уже прочитал).

4b9b3361

Ответ 1

В этом случае аннотация @ModelAttribute используется для идентификации объекта, который Spring должен добавить как атрибут модели. Атрибуты модели - это абстракция от атрибутов HttpServletRequest. В основном, это объекты, идентифицированные некоторым ключом, которые найдут свой путь в атрибутах HttpServletRequest. Вы можете сделать это, вручную добавив атрибут с помощью Model#addAttribute(String, Object), иметь аннотированный метод @ModelAttribute или аннотировать параметр метода с помощью @ModelAttribute.

То, что вам нужно понять, это то, как Spring разрешает параметры метода обработчика и вводит аргументы. Для этого используется интерфейс HandlerMethodArgumentResolver. Существует несколько классов реализации (см. Javadoc), и каждый из них несет ответственность за resolveArgument(), возвращая аргумент, который Spring будет использовать для invoke() вашего метода обработчика посредством отражения. Spring вызовет только метод resolveArgument(), если метод HandlerMethodArgumentResolver supportsParameter() возвращает true для конкретного параметра.

Реализация HandlerMethodArgumentResolver, о которой идет речь, здесь ServletModelAttributeMethodProcessor, которая простирается от ModelAttributeMethodProcessor, в котором указано

Разрешает аргументы метода, аннотированные с помощью @ModelAttribute и обрабатывает возвращает значения из методов, аннотированных с помощью @ModelAttribute.

Spring (3.2) будет зарегистрировать этот HandlerMethodArgumentResolver и другие

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

    // Annotation-based argument resolution
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());
    resolvers.add(new MapMethodProcessor());
    resolvers.add(new ErrorsMethodArgumentResolver());
    resolvers.add(new SessionStatusMethodArgumentResolver());
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }

    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));

    return resolvers;
}

Когда Spring должен вызвать метод обработчика, он будет перебирать типы параметров и через приведенный выше список и использовать первый, который supportsParameter().

Обратите внимание, что добавляются два экземпляра ServletModelAttributeMethodProcessor (один после комментария //catch all). ModelAttributeMethodProcessor имеет поле annotationNotRequired, которое сообщает ему, должно ли оно искать @ModelAttribute или нет. Первый экземпляр должен искать @ModelAttribute, второй - нет. Spring делает это так, что вы можете зарегистрировать свои собственные экземпляры HandlerMethodArgumentResolver, см. комментарий // Custom arguments.


В частности

@RequestMapping(value = "/", method = RequestMethod.POST)
public String sayHello(Person person, Model model) {
    model.addAttribute("person", person);
    return "home";
}

В этом случае не имеет значения, аннотирован ли ваш параметр Person или нет. A ModelAttributeMethodProcessor разрешит его и свяжет поля формы, т.е. параметры запроса, в поля экземпляра. Вам даже не нужно добавлять его в model, поскольку класс ModelAttributeMethodProcessor будет обрабатывать это.

В вашем методе showHelloPage()

model.addAttribute("person", new Person());

необходим с taglib <form>. То, как он разрешает свои поля input.


Итак, мой вопрос: что такое "ModelAttribute", anonnatation?

Чтобы автоматически добавить указанный параметр (или возвращаемое значение метода) в модель.

Можно ли опустить атрибут "modelAttribute" в форме?

Нет, привязка form ищет объект в model и связывает его поля с элементами html input.

И вторая часть, каков способ (возможно, аннотация), чтобы сделать форма автоматически привязывает значения входных данных к соответствующим свойствам bean(который будет объявлен как параметр метода)? Без необходимости добавления пустой bean перед отправкой формы (как я должен сделать это сейчас).

A Spring <form> тег защелкивается на объект атрибута модели и использует его поля для создания элементов input и label. Неважно, как объект оказался в модели до тех пор, пока он это сделал. Если он не может найти атрибут модели с указанным вами именем (ключом), он выдает исключения, как вы видели.

 <form:form method="post" modelAttribute="person">

Альтернативой предоставлению пустого bean является создание html самостоятельно. Все Spring <form> использует имена полей bean для создания элемента input. Итак, это

<form:form method="post" modelAttribute="person">
    <form:label path="firstName">First name</form:label>
    <form:input path="firstName" />

Создает что-то вроде

<form method="post" action="[some action url]">
    <label for="firstName">First name<label>
    <input type="text" name="firstName" value="[whatever value firstName field had]" />
    ...

Spring связывает параметры запроса с полями экземпляра с помощью атрибута name.