首页 文章

在Spring MVC 3中提交表单 - 解释

提问于
浏览
31

我在理解Spring 3 MVC中的表单提交方式时遇到了问题 .

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

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

<form:form method="post" modelAttribute="person">
    <form:label path="firstName">First name</form:label>
    <form:input path="firstName" />
    
<form:label path="lastName">Last name</form:label> <form:input path="lastName" />
<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”注释 . 我添加了它,因为在我读过的教程中,它始终存在 . 但是在我删除之后,一切都运行良好,就像之前一样 .

So my question is - "ModelAttribute" anonnatation的用途是什么?是否可以在表单中省略"modelAttribute"属性?第二部分,使表单自动绑定输入' values to the proper bean'属性(将被声明为方法参数)的方式(可能是一些注释)是什么?在发送表单之前无需添加空bean(因为我现在必须这样做) .

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

1 回答

  • 39

    在这种情况下,@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());
    

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


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

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

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

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

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

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

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

相关问题