在Spring MVC 3中提交表单 - 解释 [英] Form submit in Spring MVC 3 - explanation

查看:97
本文介绍了在Spring MVC 3中提交表单 - 解释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



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



我有一张如下所示的表格:

 < form:form method =postmodelAttribute =person> 
< form:label path =firstName>名字< / form:label>
< form:input path =firstName/>
< br />

< form:label path =lastName>姓氏< / form:label>
< form:input path =lastName/>
< br />

< input type =submitvalue =Submit/>
< / form:form>

我也有一个看起来像这样的控制器:

  @Controller 
public class HomeController {

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

@RequestMapping(value =/,method = RequestMethod.POST)
public String sayHello(Person person,Model model){
model.addAttribute (人,人);
返回家;




$ b $ p
$ b

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

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

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

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

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

来自showHelloPage方法我得到一个(共同)异常既不BindingResult也不...。



另外,一开始,sayHello方法看起来像:

 <$ c 
public String sayHello(@ModelAttribute(person)Person person,Model model){
(...)

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



所以我的问题是 - ModelAttribute anonnatation?是否有某种方式可以省略表单中的modelAttribute属性?第二部分,创建表单的方式(可能是某些注释)会自动将输入的值绑定到适当的bean的属性(将被声明为方法参数)?不需要在发送表单前添加一个空bean(因为我现在必须这样做)。



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

解决方案

@ModelAttribute 注释用于标识Spring应添加为模型属性的对象。模型属性是来自 HttpServletRequest 属性的抽象。基本上,它们是由某个键标识的对象,它们将进入 HttpServletRequest 属性。你可以通过手动添加一个属性到 Model#addAttribute(String,Object),并有一个 @ModelAttribute 注释方法,或通过使用 @ModelAttribute 注释一个方法参数。



你需要理解的是Spring如何解析您的处理程序方法参数并注入参数。它使用 HandlerMethodArgumentResolver 界面来执行此操作。有许多实现类(请参阅javadoc),每个类都有责任通过返回Spring将用于 invoke()的参数来解析 resolveArgument()通过反射您的处理程序方法。如果 HandlerMethodArgumentResolver supportsParameter() resolveArgument()方法c $ c>方法返回特定参数的 true

HandlerMethodArgumentResolver 这里的问题是 ServletModelAttributeMethodProcessor ,它从 ModelAttributeMethodProcessor 其中声明


解决方法参数注释用@ModelAttribute处理
的返回值。@ b $ b

Spring(3.2)将 register a> this HandlerMethodArg umentResolver 等等

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

//基于注解的参数解析
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()));

//基于类型的参数解析
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(新的MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(新的SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

//自定义参数
if(getCustomArgumentResolvers()!= null){
resolvers.addAll(getCustomArgumentResolvers());
}

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

返回解析器;
}

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



注意,两个 ServletModelAttributeMethodProcessor 被添加(在 //捕获所有注释之后)。 ModelAttributeMethodProcessor 有一个 annotationNotRequired 字段,告诉它它是否应该查找 @ModelAttribute 或不。第一个实例必须查找 @ModelAttribute ,第二个实例不。 Spring会这样做,以便您可以注册您自己的 HandlerMethodArgumentResolver 实例,请参阅 //自定义参数注释。






具体来说

  @RequestMapping(value =/,method = RequestMethod.POST)
public String sayHello(Person person,Model model){
model.addAttribute(person,person);
返回家;

$ / code>

在这种情况下,您的 Person 参数被注释与否。一个 ModelAttributeMethodProcessor 将解决它并绑定表单字段,即。请求参数到实例的字段。你甚至不需要将它添加到模型中,因为 ModelAttributeMethodProcessor 类将处理它。


$ b $ showHelloPage()方法

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

需要< form> 标签库。这就是它如何解决输入字段。







所以我的问题是 - ModelAttribute
anonnatation的用法是什么?


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


是否有某种方法在表单中省略modelAttribute属性?

不, form 绑定会在 Model 并将其字段绑定到html input 元素。


第二部分,创建
表单的方式(也许是一些注释)会自动将输入值绑定到适当的bean的属性
(它将被声明为一个方法参数)?无需在发送表单前添加
空bean(因为我现在必须这样做)。


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

  < form:form method =postmodelAttribute =person> 

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

 < form:form method =postmodelAttribute =person> 
< form:label path =firstName>名字< / form:label>
< form:input path =firstName/>

创建类似于

 < form method =postaction =[some action url]> 
< label for =firstName>名字< label>
< input type =textname =firstNamevalue =[无论firstName字段的值如何]/>
...

Spring使用 name 属性。


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

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:

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

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

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

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

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

(...)
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.

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

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

解决方案

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

The thing you need to understand is how Spring resolves your handler method parameters and injects arguments. It uses the HandlerMethodArgumentResolver interface 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 HandlerMethodArgumentResolver supportsParameter() method returns true for the specific parameter.

The HandlerMethodArgumentResolver implementation in question here is ServletModelAttributeMethodProcessor which extends from ModelAttributeMethodProcessor which states

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

Spring (3.2) will register this HandlerMethodArgumentResolver and others

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().

Notice that two instances of ServletModelAttributeMethodProcessor are added (one after a //catch all comment). The ModelAttributeMethodProcessor has a annotationNotRequired field which tells it if it should look for the @ModelAttribute or not. The first instance must look for @ModelAttribute, the second one doesn't. Spring does this so that you can register your own HandlerMethodArgumentResolver instances, see the // Custom arguments comment.


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 Person parameter is annotated or not. A ModelAttributeMethodProcessor will 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 model as the ModelAttributeMethodProcessor class will handle that.

In your showHelloPage() method

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

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


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

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?

No, the form binding looks for an object in the Model and binds its fields to html input elements.

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

A Spring <form> tag latches onto a model attribute object and uses its fields to create input and label elements. 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.

 <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 input element. So this

<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 name attribute.

这篇关于在Spring MVC 3中提交表单 - 解释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆