2013-09-22 84 views
30

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

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

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

<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配置文件,因为它们AR与问题无关)。

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

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

from“showHelloPage”method我得到一个(共同)异常“既不BindingResult也不...”。

此外,在开始时, “的sayHello” 方法看起来像:

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

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

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

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

回答

38

本例中的@ModelAttribute注释用于标识Spring应添加为模型属性的对象。模型属性是HttpServletRequest属性的抽象。基本上,它们是通过某个键进入HttpServletRequest属性的对象。您可以通过手动添加具有Model#addAttribute(String, Object)的属性,具有@ModelAttribute注释方法或通过使用@ModelAttribute注释方法参数来执行此操作。

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

在这里讨论的HandlerMethodArgumentResolver实现ServletModelAttributeMethodProcessorModelAttributeMethodProcessor其中规定

与@ModelAttribute注解的解决方法参数并处理从@ModelAttribute注解的方法 返回值延伸。

春季(3.2)将register这个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; 
} 

当春天需要调用你的处理方法,它会通过参数类型,并通过上面的列表中进行迭代,并使用第一个那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()); 

需要与<form>标签库。这就是它如何解决其input字段。


所以我的问题是 - 什么是使用 “的ModelAttribute” anonnatation的?

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

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

否,form结合寻找一个对象在Model并结合其字段的HTML input元件。

,第二部分,什么是方式(也许有些注释)做出 形式自动输入值绑定到适当的bean的属性 (这将被宣布为一个方法参数)?在发送表单之前不需要添加空的bean(因为我现在必须这样做)。

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

<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]" /> 
    ... 

春季结合请求参数使用name属性实例字段。

+0

非常感谢您的回复,您向我解释了很多。还有一个关于“ModelAttribute”注释的问题 - 如果我理解正确,这个注释与方法参数一起使用,相当于“model.addAttribute(...)”? –

+0

@MichałTabor尝试将其添加为方法参数。我不确定,如果因为请求没有任何可以绑定的请求参数,它会返回'null'。否则,你正在做的方式是正确的。这些被称为数据传输对象(或Spring表单支持对象/命令对象)。文档应该有更多的细节。 –

+0

+1,很好的解释 – rocketboy