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

Mic*_*bor 31 java forms spring spring-mvc

我在理解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>
Run Code Online (Sandbox Code Playgroud)

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

@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";
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<c:if test="${not empty person.firstName and not empty person.lastName}">
    Hello ${person.firstName} ${person.lastName}!
</c:if>
Run Code Online (Sandbox Code Playgroud)

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

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

model.addAttribute("person", new Person());
Run Code Online (Sandbox Code Playgroud)

从"showHelloPage"方法我得到一个(常见的)异常"既不是BindingResult也不是......".

另外,在开始时,"sayHello"方法看起来像:

(...)
public String sayHello(@ModelAttribute("person") Person person, Model model) {
(...)
Run Code Online (Sandbox Code Playgroud)

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

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

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

Sot*_*lis 39

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

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

这里讨论的HandlerMethodArgumentResolver实施是ServletModelAttributeMethodProcessorModelAttributeMethodProcessor哪些州延伸出来的

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

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;
}
Run Code Online (Sandbox Code Playgroud)

当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";
}
Run Code Online (Sandbox Code Playgroud)

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

在你的showHelloPage()方法

model.addAttribute("person", new Person());
Run Code Online (Sandbox Code Playgroud)

<form>taglib 需要它.这就是它解决其input领域的方式.


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

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

是否可以在表单中省略"modelAttribute"属性?

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

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

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

 <form:form method="post" modelAttribute="person">
Run Code Online (Sandbox Code Playgroud)

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

<form:form method="post" modelAttribute="person">
    <form:label path="firstName">First name</form:label>
    <form:input path="firstName" />
Run Code Online (Sandbox Code Playgroud)

创造类似的东西

<form method="post" action="[some action url]">
    <label for="firstName">First name<label>
    <input type="text" name="firstName" value="[whatever value firstName field had]" />
    ...
Run Code Online (Sandbox Code Playgroud)

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