【问题标题】:Form submit in Spring MVC 3 - explanationSpring MVC 3中的表单提交-解释
【发布时间】:2013-09-27 11:40:06
【问题描述】:

我在理解 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 也没有......”。

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

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

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

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

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

【问题讨论】:

    标签: java forms spring spring-mvc


    【解决方案1】:

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

    您需要了解的是 Spring 如何解析您的处理程序方法参数并注入参数。它使用HandlerMethodArgumentResolver 接口来执行此操作。有许多实现类(参见 javadoc),每个类都对 resolveArgument() 负责,通过反射将 Spring 将使用的参数返回给 invoke() 您的处理程序方法。如果HandlerMethodArgumentResolver supportsParameter() 方法为特定参数返回true,Spring 只会调用resolveArgument() 方法。

    这里有问题的HandlerMethodArgumentResolver 实现是ServletModelAttributeMethodProcessor,它从ModelAttributeMethodProcessor 扩展而来

    解析使用@ModelAttribute 注释的方法参数和句柄 从带有 @ModelAttribute 注释的方法返回值。

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

    当 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 参数是否被注释并不重要。 ModelAttributeMethodProcessor 将解决它并绑定表单字段,即。请求参数,到实例的字段。您甚至不需要将其添加到 model,因为 ModelAttributeMethodProcessor 类会处理它。

    在您的showHelloPage() 方法中

    model.addAttribute("person", new Person());
    
    &lt;form&gt; 标签库需要

    。这就是它解析其input 字段的方式。


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

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

    有没有办法在表单中省略“modelAttribute”属性?

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

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

    Spring &lt;form&gt; 标记锁定模型属性对象并使用其字段创建 inputlabel 元素。只要对象最终出现在模型中,它并不重要。如果它找不到具有您指定的名称(键)的模型属性,它会抛出异常,如您所见。

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

    提供空 bean 的替代方法是自己创建 html。所有 Spring 的 &lt;form&gt; 所做的只是使用 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 属性将请求参数绑定到实例字段。

    【讨论】:

    • 非常感谢您的回复,您为我解释了很多。关于“ModelAttribute”注释的另一个问题 - 如果我理解正确的话,这个注释与方法参数一起使用,相当于“model.addAttribute(...)”?
    • @MichałTabor 尝试将其添加为方法参数。我不确定,如果因为请求没有任何可以绑定的请求参数,它会返回null。否则,您正在做的方式是正确的方式。这些被称为数据传输对象(或 Spring 表单支持对象/命令对象)。文档应该有更多的细节。
    • @SotiriosDelimanolis 感谢您的详细解释。您能否详细说明最后一部分(提供空 bean 的替代方法)?在这种情况下,由于form 标签上没有提供modelAttribute,框架如何将form 值绑定到@RequestMapping 方法上的命令参数?它只是尝试使用方法中存在的bean吗?
    • @pasemes 注意&lt;form&gt;&lt;form:form&gt; 之间的区别。前者是 HTML 元素。后者是Spring提供的form标签库的form标签。当 Spring form 被渲染时,它会生成一个 HTML form 并带有转换后的 &lt;input&gt; 名称。使用 HTML 表单,您必须手动完成。
    • @pasemes 哦,取决于。它查看参数类型(从中创建名称)或@ModelAttribute 的名称,如果存在这样的属性,它会检查请求属性。如果没有,它会创建并添加一个。然后它使用各种属性并将它们绑定到该属性对象。
    猜你喜欢
    • 2012-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-05
    • 1970-01-01
    相关资源
    最近更新 更多