Spring:绑定对象有和没有@ModelAttribute

Posted

技术标签:

【中文标题】Spring:绑定对象有和没有@ModelAttribute【英文标题】:Spring : binding object with and without @ModelAttribute 【发布时间】:2017-07-16 16:35:03 【问题描述】:

我是 Spring 的新手,正在注册一个用户。我确实喜欢这个。

@RequestMapping("/register")
    public String register(@ModelAttribute User user,BindingResult result)
       if(!result.hasErrors())
         userSerive.register(user);
       
     return "welcome";

这很好用,但这里的问题是我的welcome.jsp 页面中不需要这个user 对象,所以为什么要让模型对象更重。所以我尝试不使用@ModelAttribute,这也适用于我,如下所示.

@RequestMapping("/register")
    public String register(User user,BindingResult result)
       if(!result.hasErrors())
         userSerive.register(user);
       
     return "welcome";

所以我只想知道两者的优缺点和如果我真的不需要jsp 中的user 对象,这是最佳实践。@ModelAttribute 需要除了将对象添加到模型之外,还有哪些 spring 隐式绑定没有。@ModelAttribute 是更安全的绑定方式还是其他方式?

我想将我的查询分类为以下 4 种类型的请求。如果我不需要在视图中发送数据并且我的请求是以下任何一种,那么使用和不使用 @ModelAttribute 会有什么区别-

    查询字符串,即GET中的表单数据 请求有效负载或正文,即 POST 中的表单数据 ajaxified GET 请求中的 json 数据 POST 请求中的 json 数据 - 我想这不会在两者中的任何一个中绑定。 @RequestBody 为必填项。

【问题讨论】:

你不能在这个问题上开始赏金,因为赏金每次都必须升值,下一个增量是 100 代表。 【参考方案1】:

在您的情况下,两个方法签名之间的行为可能(见下文...)没有区别。

两者都将请求参数绑定到user,并将生成的对象作为属性user添加到模型中——该属性名称派生自方法参数的非大写类型名称User

@ModelAttribute 可用于自定义属性名称,例如@ModelAttribute("theUser"),或者向代码的读者提示视图中使用了此参数。但正如您所说,这些都不适用于您的用例。

无论您是否使用 @ModelAttribute 注释,Spring 中完全相同的代码都将用于填充参数 - 有问题的代码是 org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor

因此对我来说,在代码中使用public String register(User user, BindingResult result) 签名更有意义。向模型中不需要的方法参数添加 @ModelAttribute 注释可能会使阅读您的代码的人感到困惑。


稍长一点的答案是,差不多在你的情况下指定@ModelAttribute 是有原因的 - 但它非常神秘且不太可能。

Spring 处理程序方法中的方法参数由HandlerMethodArgumentResolver 实例填充。这些是可配置的,并为每个参数依次尝试。

默认处理程序方法参数解析器如下所示(参见RequestMappingHandlerAdapter):

resolvers.add(new ServletModelAttributeMethodProcessor(false));

...

resolvers.add(new ServletModelAttributeMethodProcessor(true));

如果您要在中间添加自己的,例如UserHandlerMethodArgumentResolver,然后您可以使用 @ModelAttribute 告诉 Spring 以默认方式处理特定参数,而不是使用您的自定义参数解析器类。

【讨论】:

这是 OP 问题的答案。【参考方案2】:

这个问题很有用,但是我看这里的回复没有正确回答这个问题。

我阅读了 *** 中的更多线程,发现这个非常有用: https://***.com/a/26916920/1542363

对于我自己如何决定使用哪一个,如果我只需要绑定并且不想将参数对象存储在模型中,那么不要使用@ModelAttribute

【讨论】:

除了发送到jsp之类的视图之外,我还没有找到将参数对象存储在模型中的任何原因。是的,我的问题是关于是否有任何其他原因。【参考方案3】:

除了将对象添加到模型之外,Spring MVC 还使用它将绑定对象提供给控制器方法,您可以在其中使用它,在您的情况下“注册”。

是的,@ModelAtttribute 是 Spring MVC 中将传入的帖子数据绑定到对象的最安全和最好的方法。

【讨论】:

【参考方案4】:

查看此帖子here。它涵盖了有关 ModelAttribute 的大量细节。

ModelAttribute 只能与表单编码的请求数据一起使用。它无法将 json/xml 请求数据与数据对象绑定。为此,您必须使用 RequestBody。

【讨论】:

【参考方案5】:

除了@ryanp 的完美答案,我想补充一下:

对于现代的spring mvc项目,肯定会使用@Controller和@RequestMapping等注解来提供请求处理程序,在内部,Spring MVC使用RequestMappingHandlerAdapter.invokeHandlerMethod()来处理用户提供的HandlerMethod的请求。如果您查看 RequestMappingHandlerAdapter,它会设置参数解析器的集合来为 HandlerMethod 准备参数,通过查看该集合,您会了解 Spring MVC 解析请求并填充用户提供的参数的方式和顺序。所以这里是源代码:

```Java

/**
 * Return the list of argument resolvers to use including built-in resolvers
 * and custom resolvers provided via @link #setCustomArgumentResolvers.
 */
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 PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    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;

```

值得注意的是底部的 Catch-all 解析器。 Spring MVC 使用处理 @RequestParam 和 @ModelAttribute 的两个解析器来分别处理无注释的简单类型和 pojo 类型参数。这就是为什么在OP的测试中,有没有@ModelAttribute并不重要。

很遗憾,它在 Spring MVC 的参考中没有明确说明。

【讨论】:

【参考方案6】:

查看当前 (Spring 5.1.5) 文档 (https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-arguments):

控制器方法参数 - 任何其他参数 [GP - 无注释]:

If a method argument is not matched to any of the earlier values in this table and
it is a simple type (as determined by BeanUtils#isSimpleProperty),
it is a resolved as a @RequestParam. Otherwise, it is resolved as a @ModelAttribute.

因此,如果您在控制器映射方法中有一个非简单属性作为参数,则完全等同于将其注释为 @ModelAttribute

【讨论】:

【参考方案7】:

如 Spring MVC 文档中所述 - @ModelAttribute 注解可用于方法或方法参数。当然,我们可以在一个控制器中同时使用两者。

方法注释

@ModelAttribute("person")
public Person getPerson()
    return new Person();

这种方法的目的是在模型中添加属性。所以在我们的例子中,person 键将 person 对象作为模型中的值。在同一控制器中,在 @RequestMapping 方法之前调用控制器中的 @ModelAttribute 方法。

方法参数

public String processForm(@ModelAttribute("person") Person person)
    person.getStuff();

参见 spring 文档http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib-method-args

【讨论】:

您缺少上下文。根据我的用例,我不需要视图层中的模型,我可以使用和不使用@ModelAttribute 在 java 中绑定 formdata,如果我们不需要,这两种方法有什么区别这个模型在 jsp 中。

以上是关于Spring:绑定对象有和没有@ModelAttribute的主要内容,如果未能解决你的问题,请参考以下文章

java问题:有个类Student Student stu=null 与Student stu= new Student()有和区别

JavaScript中啥是prototype原型对象?它有和作用?

绑定Spring MVC命令对象时如何自定义参数名称?

将子类对象绑定到spring mvc jsp视图

Spring的@RequestParam对象绑定

Spring + Thymeleaf 绑定表单 NullpointerException