Spring MVC:请解释@RequestParam和@ModelAttribute之间的区别

时间:2015-03-31 14:07:37

标签: java spring spring-mvc spring-annotations

我是Spring MVC的新手。请帮我解开文档。

文档

Spring MVC Documentation州(强调我的):

    方法参数上的
  • @ModelAttribute表示应该从模型中检索参数。如果模型中不存在,则应首先实例化参数,然后将其添加到模型中。一旦出现在模型中,参数的字段应该从所有具有匹配名称的请求参数中填充。 WebDataBinder类将请求参数名称(包括查询字符串参数和表单字段)与名称模型属性字段进行匹配。

  • @RequestParam 将请求参数绑定到控制器中的方法参数

免责声明/澄清者

我知道@ModelAttribute@RequestParam不是一回事,互不排斥,不执行相同的角色,可以同时使用,如this question - 确实,@RequestParam可用于填充@ModelAttribute的字段。我的问题更多地针对其内部运作之间的差异。

问题:

@ModelAttribute(用于方法参数,而不是方法)和@RequestParam之间有什么区别?具体做法是:

  • 来源: @RequestParam@ModelAttribute具有相同的来源 信息/人口,即URL中的请求参数,可能是作为POST的表单/模型的元素提供的?
  • 用法:使用@RequestParam检索的变量被丢弃是正确的(除非传递给模型),而使用@ModelAttribute检索的变量会自动输入到模型中要退回吗?

或者在非常基本的编码示例中,这两个示例之间真正的工作差异是什么?

示例1:@RequestParam

// foo and bar are thrown away, and are just used (e.g.) to control flow?
@RequestMapping(method = RequestMethod.POST)
public String testFooBar(@RequestParam("foo") String foo,
@RequestParam("bar") String bar, ModelMap model) {
    try {
     doStuff(foo, bar);
    }
    // other code
  }

示例2:@ModelAttribute

// FOOBAR CLASS
// Fields could of course be explicitly populated from parameters by @RequestParam
public class FooBar{
    private String foo;
    private String bar;
   // plus set() and get() methods
}

// CONTROLLER
// Foo and Bar become part of the model to be returned for the next view?
@RequestMapping(method = RequestMethod.POST)
public String setupForm(@ModelAttribute("fooBar") FooBar foobar) {
   String foo = fooBar.getFoo();
   String bar = fooBar.getBar();
   try {
      doStuff(foo, bar);
   }
   // other code
}

我目前的理解:

@ModelAttribute@RequestParam都会询问请求参数以获取信息,但他们会以不同方式使用此信息:

  • @RequestParam只填充独立变量(当然可能是@ModelAttribute类中的字段)。完成控制器后,这些变量将被丢弃,除非它们已被送入模型。

  • @ModelAttribute填充类的字段,然后填充要传递回视图的模型的属性

这是对的吗?

5 个答案:

答案 0 :(得分:35)

  

@RequestParam只填充独立变量(当然可能是@ModelAttribute类中的字段)。完成控制器后,这些变量将被丢弃,除非它们已被送入模型。

不要混淆"模型"与会话。 http对话通常为:HTTP.GET,服务器响应,然后是HTTP.POST。当你使用@ModelAttribute注释时,你总是在构建一个你注释过的任何实例,这就是让你认为向模型提供东西的原因。可能会让变量四处游走。这是不正确的,一旦HttpServletRequest完成,这些变量就不应再成为浏览器/服务器对话的一部分,除非它们已保存在会话中。

  

@ModelAttribute填充类的字段,然后填充要传递回视图的模型的属性

是的!为了正确,@ModelAttribute告诉Spring使用其默认的Web数据绑定器来填充来自HttpServletRequest的数据的实例。选择将此数据传递回视图取决于程序员。当您使用@ModelAttribute注释的方法时,每次代码命中该servlet时都会调用它。当您使用@ModelAttribute作为方法参数之一时,我们正在讨论传入的Http表单数据绑定。

拨打@RequestParam是说出request.getParameter("foo")的捷径;在引擎盖下,Java HttpServletRequest允许您通过执行key->值查找来从请求对象获取值。返回的值是Object类型。如果您没有在Web应用程序中使用Spring,那么这就是您要输入的内容。

当您开始使用@ModelAttribute时,Spring会将此抽象更进一步。该注释采用数据绑定的概念。数据绑定的目标是控制器中的代码不必为每个表单元素调用request.getParameter("foo1")。想象一下,你有一个包含5个字段的Web表单。如果没有数据绑定,程序员必须手动检索并验证每个字段。程序员必须确保请求包含属性,属性的值存在,以及属性的值是每个字段的预期类型。使用@ModelAttribute告诉Spring为您完成这项工作。

如果使用@ModelAttribute("fooBar") FooBar fooBar在控制器中注释方法,则FooBar的实例将总是由Spring构造,并提供给您的方法。数据绑定发挥作用的地方是在Method的参数中使用此注释时; Spring查看HttpServletRequest的实例,并查看它是否可以将请求中的数据与FooBar的实例上的右属性相匹配。这基于java属性约定,其中包含foo字段和名为getFoosetFoo的公共getter和setter。这可能看起来很神奇,但是如果你违反惯例,你的Spring数据绑定将停止工作,因为它无法知道 where 绑定来自HttpServletRequest你的数据仍会获得FooBar的实例,但不会将属性设置为请求中的任何值。

答案 1 :(得分:3)

@ModelAttribute:绑定整个Java对象(如Employee)。支持多个请求参数

@RequestParam:绑定单个请求参数(如firstName)

一般来说,
@RequestParam最适合阅读少数参数。

当您拥有包含大量字段的表单时,将使用

@ModelAttribute

@ModelAttribute为您提供其他功能,例如数据绑定,验证和表单预填充。

答案 2 :(得分:2)

@ModelAttribute带注释的参数由注册的ServletModelAttributeMethodProcessor(或ModelAttributeMethodProcessor)处理,@RequestParam带注释的参数由注册的RequestParamMethodArgumentResolver或{{1}处理取决于参数类型。

以下解释了Spring如何使用这些RequestParamMapMethodArgumentResolver来解析处理程序方法的参数:

HandlerMethodArgumentResolvers@ModelAttribute这两种情况下,要绑定的值都是从ServletRequest parameters中检索的。

您可以查看上述类型的源代码,但这里有简单的细节。

对于@RequestParam,Spring将创建参数类型的实例。它将检查该实例的字段,并尝试根据由@ModelAttribute名称和字段名称组成的命名/别名策略将参数值绑定到它们。它通常使用一组@ModelAttribute个实例从Converter(参数值始终为String值)转换为目标字段类型StringInteger,您也可以注册自己的Date类型进行自定义转换。您也可以嵌套POJO类型。

对于Converter,Spring将使用相同的@RequestParam实例将参数值直接转换为带注释的参数类型。

请注意,参数值不会被“丢弃”。它们在容器请求处理周期的持续时间内存储在Converter中。您始终可以通过appropriate methods

访问它们

答案 3 :(得分:0)

@ModelAttribute (参数)@SessionAttributes@ModelAttribute (方法)加载模型属性。

您不仅需要绑定请求中的值,而且从@SessionAttributes加载后也会这样做。

@RequestParam将请求参数绑定到对象。

答案 4 :(得分:0)

  • 在方法级别

在方法级别使用注释时,它表示该方法的目的是添加一个或多个模型属性。此类方法支持与@RequestMapping方法相同的参数类型,但不能直接映射到请求。

@ModelAttribute
public void addAttributes(Model model) {
    model.addAttribute("msg", "Welcome to the Netherlands!");
}

将名为msg的属性添加到控制器类中定义的所有模型的方法。

在调用任何请求处理程序方法之前,Spring-MVC将始终首先调用该方法。 也就是说,在调用使用@RequestMapping注释的控制器方法之前调用@ModelAttribute方法。序列背后的逻辑是,必须在控制器方法内部任何处理开始之前创建模型对象。

将相应的类注释为@ControllerAdvice也很重要。因此,您可以在Model中添加将被标识为全局的值。这实际上意味着对于每个请求,对于响应部分中的每个方法都存在默认值。

  • 作为方法论据

当用作方法参数时,它表示应从模型中检索参数。如果不存在,则应首先将其实例化,然后添加到模型中,并且一旦出现在模型中,就应从所有具有匹配名称的请求参数中填充参数字段。

在随后的代码段中,用提交到addUser端点的表单中的数据填充用户模型属性。 Spring MVC在调用submit方法之前在幕后执行此操作:

**@RequestMapping**(value = "/addUser", method = RequestMethod.POST)
public String submit(@ModelAttribute("user") User user) {
    return "userView";
}

因此,它将表单数据与bean绑定。使用@RequestMapping注释的控制器可以使用@ModelAttribute注释自定义类参数。

这就是Spring-MVC中通常所说的数据绑定,这是一种常见的机制,可以使您不必单独解析每个表单字段。