Java Spring MVC 3 中的表单提交 - 解释

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18944627/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 12:47:45  来源:igfitidea点击:

Form submit in Spring MVC 3 - explanation

javaformsspringspring-mvc

提问by Micha? Tabor

I'm having problems understanding how does a form submit in Spring 3 MVC work.

我在理解 Spring 3 MVC 中提交的表单如何工作时遇到问题。

What I want to do, is to create a controller which would take the user's name and display it to him. And somehow I have done it but I don't really understand how it works. So..

我想要做的是创建一个控制器,该控制器将采用用户的姓名并将其显示给他。不知何故,我已经做到了,但我真的不明白它是如何工作的。所以..

I have a form which looks like this:

我有一个看起来像这样的表格:

<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>

I also have a controller which looks like this:

我还有一个控制器,看起来像这样:

@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";
    }
}

To display a welcome message to a user I use the following code in the JSP page:

要向用户显示欢迎消息,我在 JSP 页面中使用以下代码:

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

And it works (I omit the XML configuration files because they are irrelevant to the problem).

它有效(我省略了 XML 配置文件,因为它们与问题无关)。

I thought that the "modelAttribute" attribute in a form points to the bean variable which should be populated with inputs' values (as set in their "path" attributes). But looks, it works in a very different way. If I remove the line

我认为表单中的“modelAttribute”属性指向应该用输入值填充的 bean 变量(在它们的“路径”属性中设置)。但看起来,它的工作方式非常不同。如果我删除该行

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

from "showHelloPage" method I get an (common) exception "Neither BindingResult nor...".

从“showHelloPage”方法我得到一个(常见的)异常“既没有BindingResult也没有......”。

Also, on the beginning, the "sayHello" method looked like:

此外,一开始,“sayHello”方法看起来像:

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

I mean, it had the "ModelAttribute" annotation. I added it, because in the tutorials I have read, it was always present. But after I removed it, everything worked well, as it did before.

我的意思是,它有“ModelAttribute”注释。我添加了它,因为在我阅读的教程中,它始终存在。但是在我删除它之后,一切都运行良好,就像以前一样。

So my question is- what is the use of the "ModelAttribute" anonnatation? Is it some way to omit a "modelAttribute" attribute in a form? And the second part, what is the way (maybe some annotation) to make a form automatically bind inputs' values to the proper bean's properties (which would be declared as a method parameter)? Without a need of adding an empty bean before sending a form (as I have to do it now).

所以我的问题是- “ModelAttribute”注释的用途什么?是否有某种方法可以省略表单中的“modelAttribute”属性?第二部分,有什么方法(可能是一些注释)使表单自动将输入的值绑定到适当的 bean 的属性(将声明为方法参数)?在发送表单之前无需添加空 bean(因为我现在必须这样做)。

Thanks for your replies (which aren't links to the Spring's documentation, because I have already read it).

感谢您的回复(不是 Spring 文档的链接,因为我已经阅读了它)。

采纳答案by Sotirios Delimanolis

The @ModelAttributeannotation in this case is used to identify an object that Spring should add as a model attribute. Model attributes are an abstraction from the HttpServletRequestattributes. Basically, they are objects identified by some key that will find their way into the HttpServletRequestattributes. You can do this by manually adding an attribute with Model#addAttribute(String, Object), have a @ModelAttributeannotated method, or by annotating a method parameter with @ModelAttribute.

@ModelAttribute本例中的注解用于标识 Spring 应添加为模型属性的对象。模型属性是对属性的抽象HttpServletRequest。基本上,它们是由一些键标识的对象,这些键会找到进入HttpServletRequest属性的方式。您可以通过使用 手动添加属性Model#addAttribute(String, Object)、使用带@ModelAttribute注释的方法或使用 来注释方法参数来完成此操作@ModelAttribute

The thing you need to understand is how Spring resolves your handler method parameters and injects arguments. It uses the HandlerMethodArgumentResolverinterface to do so. There are a number of implementing classes (see javadoc) and each has the responsibility to resolveArgument()by returning the argument that Spring will use to invoke()your handler method through reflection. Spring will only call the resolveArgument()method if the HandlerMethodArgumentResolversupportsParameter()method returns truefor the specific parameter.

您需要了解的是 Spring 如何解析您的处理程序方法参数并注入参数。它使用HandlerMethodArgumentResolver接口来做到这一点。有许多实现类(参见 javadoc),每个类都有责任通过反射resolveArgument()将 Spring 将使用的参数返回给invoke()您的处理程序方法。resolveArgument()如果该HandlerMethodArgumentResolversupportsParameter()方法true为特定参数返回,Spring 只会调用该方法。

The HandlerMethodArgumentResolverimplementation in question here is ServletModelAttributeMethodProcessorwhich extends from ModelAttributeMethodProcessorwhich states

HandlerMethodArgumentResolver这里所讨论的实现是ServletModelAttributeMethodProcessorModelAttributeMethodProcessor哪个州延伸出来的

Resolves method arguments annotated with @ModelAttribute and handles return values from methods annotated with @ModelAttribute.

解析用@ModelAttribute 注释的方法参数,并处理用@ModelAttribute 注释的方法的返回值。

Spring (3.2) will registerthis HandlerMethodArgumentResolverand others

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;
}

When Spring needs to invoke your handler method, it'll iterate through the parameter types and through the above list and use the first one that supportsParameter().

当 Spring 需要调用您的处理程序方法时,它将遍历参数类型和上面的列表,并使用第一个supportsParameter()

Notice that two instances of ServletModelAttributeMethodProcessorare added (one after a //catch allcomment). The ModelAttributeMethodProcessorhas a annotationNotRequiredfield which tells it if it should look for the @ModelAttributeor not. The first instance must look for @ModelAttribute, the second one doesn't. Spring does this so that you can register your own HandlerMethodArgumentResolverinstances, see the // Custom argumentscomment.

请注意,ServletModelAttributeMethodProcessor添加了两个实例(一个在//catch all注释之后)。TheModelAttributeMethodProcessor有一个annotationNotRequired字段,告诉它是否应该查找@ModelAttribute。第一个实例必须寻找@ModelAttribute,第二个没有。Spring 这样做是为了让您可以注册自己的HandlerMethodArgumentResolver实例,请参阅// Custom arguments注释。



Specifically

具体来说

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

In this case, it doesn't matter if your Personparameter is annotated or not. A ModelAttributeMethodProcessorwill resolve it and bind form fields, ie. request parameters, to the fields of the instance. You shouldn't even need to add it to the modelas the ModelAttributeMethodProcessorclass will handle that.

在这种情况下,您的Person参数是否被注释并不重要。AModelAttributeMethodProcessor将解析它并绑定表单字段,即。请求参数,到实例的字段。您甚至不需要将它添加到类中,model因为ModelAttributeMethodProcessor类会处理它。

In your showHelloPage()method

在你的showHelloPage()方法中

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

is needed with the <form>taglib. That's how it resolves its inputfields.

需要使用<form>taglib。这就是它解析input字段的方式。



So my question is - what is the use of the "ModelAttribute" anonnatation?

所以我的问题是 - “ModelAttribute”注释的用途是什么?

To automatically add the specified parameter (or method return value) to the model.

自动将指定的参数(或方法返回值)添加到模型中。

Is it some way to omit a "modelAttribute" attribute in a form?

是否有某种方法可以省略表单中的“modelAttribute”属性?

No, the formbinding looks for an object in the Modeland binds its fields to html inputelements.

不,form绑定会在 中查找对象Model并将其字段绑定到 htmlinput元素。

And the second part, what is the way (maybe some annotation) to make a form automatically bind inputs' values to the proper bean's properties (which would be declared as a method parameter)? Without a need of adding an empty bean before sending a form (as I have to do it now).

第二部分,有什么方法(可能是一些注释)使表单自动将输入的值绑定到正确的 bean 的属性(将声明为方法参数)?在发送表单之前无需添加空 bean(因为我现在必须这样做)。

A Spring <form>tag latches onto a model attribute object and uses its fields to create inputand labelelements. It doesn't matter how the object ended up in the model as long as it did. If it can't find a model attribute with the name (key) you specified, it throws exceptions, as you saw.

Spring<form>标记锁定到模型属性对象并使用其字段来创建inputlabel元素。对象如何在模型中结束并不重要,只要它这样做了。如果它找不到具有您指定的名称(键)的模型属性,则会抛出异常,如您所见。

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

The alternative to providing an empty bean is to create the html yourself. All Spring's <form>does is use the bean's field names to create an inputelement. So this

提供空 bean 的替代方法是自己创建 html。Spring<form>所做的就是使用 bean 的字段名称来创建一个input元素。所以这

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

Creates something like

创建类似的东西

<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 binds request parameters to instance fields using the nameattribute.

Spring 使用name属性将请求参数绑定到实例字段。