Spring MVC中的@ModelAttribute是什么?

434

@ModelAttribute 在 Spring MVC 中的目的和用途是什么?


52
阅读文档:http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib 和 http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html要求翻译为:请查阅文档:http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib 和 http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html - skaffman
51
我认为这是一个有用的问题,因为它让读者获得比Spring官方文档更多的信息(包括示例)。 - anton1980
4
请查看此篇文章:http://thespringthing.blogspot.com/2010/11/how-does-modelattribute-work.html - praveenj
14个回答

438

@ModelAttribute 是指 Model 对象中的一个属性(在 MVC 中的 M); 因此,假设我们有一个名为 "Person" 的表单数据绑定对象,你可以通过使用 @ModelAttribute 注解,让 Spring MVC 将这个对象提供给 Controller 方法:

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

另一方面,注释用于定义应该成为模型一部分的对象。因此,如果您想在模型中引用一个名为Person的对象,可以使用以下方法:

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

这个有注释的方法将允许您在视图中访问Person对象,因为它会被Spring自动添加到Models中。

请参见“使用 @ModelAttribute”


11
在你的第一个情况下,实际上不需要使用 @ModelAttribute - Neil McGuigan
2
@Neil,什么时候需要在方法属性中使用@ModelAttribute - Ryan
5
@Ryan 请参考 https://dev59.com/lWoy5IYBdhLWcg3wPLn0#26916920 - Neil McGuigan
2
你应该包括如何填充Person以及如何调用此方法。 - Philip Rego
仅仅讨论概念是不够的,更好的方式是深入细节。例如,我们可以探讨@ModelAttribute和没有注释参数之间的区别,以及何时需要使用它们。 - Amir

153

我知道这是一个旧的线程,但我想参与讨论并看看能否让事情更加混乱 :)

我发现最初理解@ModelAttribute的难点在于Spring决定将多个注释组合成一个。一旦我将其拆分为几个较小的注释,它就变得更清晰了:

对于参数注释,请将@ModelAttribute视为相当于@Autowired + @Qualifier,即它尝试从Spring管理的模型中检索具有给定名称的bean。如果未找到命名bean,则它会隐式地承担@Bean的角色,即使用默认构造函数创建一个新实例并将该bean添加到模型中,而不是抛出错误或返回null

对于方法注释,请将@ModelAttribute视为相当于@Bean + @Before,即将用户代码构建的bean放入模型中,并始终在请求处理方法之前调用。

形象地说,我将@ModelAttribute视为以下内容(请不要字面理解!!):

@Bean("person")
@Before
public Person createPerson(){
  return new Person();
}

@RequestMapping(...)
public xxx handlePersonRequest( (@Autowired @Qualifier("person") | @Bean("person")) Person person, xxx){
  ...
}

正如你所看到的,Spring 做出了明智的决定,将@ModelAttribute注解作为一种全面包含的注解;没有人想看到注解自助餐。

As you can see, Spring made the right decision to make @ModelAttribute an all-encompassing annotation; no one wants to see an annotation smorgasbord.

3
嗯,“@Bean”默认是单例的。我不确定这里是否适用相同的概念。 - Zombies
11
绝对不是的。我只是使用更简单的注解来解释这个复杂的注解。请从概念上理解我的解释,而非字面上理解。 - Christopher Yang
5
@Zombies 后面加上 @Scope("request") 就可以了 :) - OrangeDog

38

那我会尽力用简单易懂的语言来解释。我们来看看:

public class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }
}

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

1. 方法注解

@ModelAttribute("cities")
 public List<String> checkOptions(){
 return new Arrays.asList(new[]{"Sofia","Pleven","Ruse"});//and so on
}

这种方法的目的是在模型中添加属性。因此,在我们的情况下,“cities”键将具有列表new Arrays.asList(new[]{"Sofia","Pleven","Ruse"})作为模型中的值(您可以将模型视为映射(键:值))。控制器中的@ModelAttribute方法在同一控制器内的@RequestMapping方法之前被调用。

在这里,我们想要向模型中添加常见信息,该信息将用于表单以向用户显示。例如,它可用于填充HTML select:

enter image description here

2.方法参数

public String findPerson(@ModelAttriute(value="person") Person person) {
    //..Some logic with person
    return "person.jsp";
}

方法参数上的@ModelAttribute表示该参数应从模型中检索。因此,在这种情况下,我们期望在模型中有person对象作为键,并且我们想获取其值并将其放入方法参数Person person中。如果不存在这样的对象或者(有时您会拼错(value="persson")),那么Spring将无法在模型中找到它,并使用其默认值创建空的Person对象。然后,将获取请求参数,并尝试使用它们的名称将它们绑定到Person对象中。

name="Dmitrij"&countries=Lesoto&sponsor.organization="SilkRoad"&authorizedFunds=&authorizedHours=&

我们有一个名字,它将使用setName(String name)绑定到Person.name。因此,在

//..Some logic with person

我们可以访问该填充名称,其值为“Dimitrij”。
当然,Spring可以绑定更复杂的对象,如列表、映射、列表集合等,但在幕后它会进行数据绑定。
我们可以同时拥有带有@ModelAttribute参数的模型注释方法和请求方法处理程序。然后我们必须合并规则。
当然,我们有大量不同的情况 - @ModelAttribute方法也可以在@ControllerAdvice中定义等等...

34

对于我的编程风格,我总是使用@ModelAttribute从Spring表单JSP中获取对象。例如,我在JSP页面上设计表单时,该表单存在commandName属性。

<form:form commandName="Book" action="" methon="post">
      <form:input type="text" path="title"></form:input>
</form:form>

然后我使用以下代码在控制器上捕获对象:

and I catch the object on controller with follow code

public String controllerPost(@ModelAttribute("Book") Book book)
每个书籍的字段名都必须与表单子元素路径匹配。

6
“catch”动词恰当地描述了“@ModelAttribute”所完成的工作。很好。 - Eddy
3
年度最佳答案。 - Jupiter Cls
5
有必要吗?在不使用 @ModelAttribute 注解的情况下,它仍然能正常工作。 - user8710021
modelAttribute是新的方式;commandName是旧的方式。https://dev59.com/3WEi5IYBdhLWcg3wNqHN - Nor.Z

34

我知道我来晚了,但正如他们所说的那样,“迟到总比不到好”。所以让我们开始吧, 每个人都有自己解释事物的方式,让我用一个例子尝试通过几个步骤为您概括和简化它; 假设您有一个简单的表单 form.jsp

<form:form action="processForm" modelAttribute="student">
  First Name : <form:input path="firstName" /> 
  <br/><br/>
  Last Name : <form:input path="lastName" />
  <br/><br/>
  <input type="submit" value="submit"/>
</form:form>
```html

<form:input path="firstName" /> <form:input path="lastName" /> 这些是 Student 类中的字段/属性。当表单被调用/初始化时,会调用它们的 getter 方法。在表单提交时,会调用它们的 setter 方法,并将它们的值传递到在表单标签中使用 modelAttribute="student" 指定的 bean 中。

我们有一个包括以下方法的 StudentController:

```
@RequestMapping("/showForm")
// `Model` is used to pass data between controllers and views
public String showForm(Model theModel) {
    // attribute name, value
    theModel.addAttribute("student", new Student());
    return "form";
}
   
@RequestMapping("/processForm")
public String processForm(@ModelAttribute("student") Student theStudent) {
    System.out.println("theStudent :"+ theStudent.getLastName());
    return "form-details";
}

//@ModelAttribute("student") Student theStudent
//Spring automatically populates the object data with form data 
//all behind the scenes

现在我们终于有了一个form-details.jsp

<b>Student Information</b>
${student.firstName}
${student.lastName}

那么问题来了,Spring MVC中的@ModelAttribute是什么?以下是源自http://www.baeldung.com/spring-mvc-and-the-modelattribute-annotation的定义:

@ModelAttribute是一种注释,将方法参数或方法返回值绑定到命名模型属性,然后将其公开给Web视图。

它实际上是获取由表单提交的所有值并保存它们以便将它们绑定或分配给对象。 它的工作方式类似于@RequestParameter,只是我们只获取一个参数并将值分配给某个方法参数。

不同之处在于@ModelAttribute包含所有表单数据而不是单个参数。 它为您创建一个bean,其中包含在表单中提交的数据。

回顾整个过程:

步骤1:
发送请求并运行我们的方法showForm()时,设置一个名称为student的模型,一个临时的bean,并将其转发到表单:
theModel.addAttribute("student", new Student());

步骤2:
表单属性modelAttribute="student"定义在表单提交时,模型将更新学生并保存表单的所有参数。

步骤3:
在表单提交时,使用参数@ModelAttribute("student") Student theStudent调用processForm()方法:获取带有modelAttribute="student"的表单中保存的值,并将其分配给Student对象中的字段。

步骤4:
然后我们可以按照我们的意愿使用它,就像在页面上显示它一样,就像我做的那样。

希望这有助于您理解这个概念。谢谢


6
非常清晰易懂的解释,这正是我在互联网上一直寻找的。感谢上帝,我发现了您的回复。 - Naveen kumar
1
到目前为止最好的解释 - Rahul
好的解释。但是方法级别上的@ModelAttribute呢? - Skod

16

无论是 Gmail、Facebook 还是 Instagram,或者任何其他 Web 应用程序,它们都是有关端用户和应用程序之间或 UI 和后端应用程序之间的数据或信息交换。即使在 Spring MVC 的世界中,也有两种交换数据的方式:

  1. 从控制器到 UI,以及
  2. 从 UI 到控制器。

我们感兴趣的是数据如何从 UI 传递到控制器。这也可以通过以下两种方式来实现:

  1. 使用 HTML 表单
  2. 使用查询参数。

使用 HTML 表单:请考虑以下场景,

Form Submission Representation

当我们从 Web 浏览器提交表单数据时,我们可以将该数据作为对象在 Controller 类中访问。当我们提交 HTML 表单时,Spring 容器会执行四个操作。它会,

  1. 首先使用 request.getParameter 方法读取请求中提交的所有数据。
  2. 一旦读取,它将使用 integer.parseIntdouble.parseDouble 和所有其他可用的解析方法根据数据类型转换为相应的 Java 类型。
  3. 一旦解析完成,它将创建我们创建的模型类的一个对象。例如,在此场景中,提交的是用户信息,我们创建了一个名为 User 的类,容器会创建该对象,并自动将所有值设置到该对象中。
  4. 然后,通过将这些值设置到控制器,它将该对象移交给控制器。

要使整个过程能够工作,我们需要遵循某些步骤。

Internal working

我们首先需要定义一个模型类,比如 User,在其中字段的数量应与 HTML 表单中的字段数量完全匹配。而且,我们在 HTML 表单中使用的名称应与我们在 Java 类中使用的名称匹配。这两点非常重要。名称应匹配,表单中的字段数量应与我们创建的类中的字段数量匹配。一旦我们做到了这一点,容器将自动读取传入的数据,创建此模型对象,设置其值并将其移交给 Controller。为了在 Controller 中读取这些值,我们在方法参数上使用 @ModelAttribute 注释。当我们在 Controller 中创建方法时,我们将使用 @ModelAttribute 并添加一个参数,该参数将自动由容器提供给我们。

以下是一个注册用户的示例代码:

@RequestMapping(value = "registerUser", method = RequestMethod.POST)
public String registerUser(@ModelAttribute("user") User user, ModelMap model) {
    model.addAttribute("user", user);
    return "regResult";
}
希望这个图解能够帮助到您!

需要使用line mode.AddAttribute("user", user)吗?因为通过@ModelAttribute("user"),我认为参数会自动绑定到模型属性"user"。 - Nick Wills

4

这是在Spring MVC中用于数据绑定的。让我们假设你有一个包含表单元素的JSP页面,例如:

JSP 上:

<form:form action="test-example" method="POST" commandName="testModelAttribute"></form:form>

(Spring Form方法,也可以使用简单的表单元素)

在控制器端:

@RequestMapping(value = "/test-example", method = RequestMethod.POST)
public ModelAndView testExample(@ModelAttribute("testModelAttribute") TestModel testModel, Map<String, Object> map,...) {

}

现在,当您提交表单时,表单字段的值就会变得可用。

4

4

将方法参数或方法返回值绑定到命名的模型属性的注释,暴露给Web视图。

public String add(@ModelAttribute("specified") Model model) {
    ...
}

1

@ModelAttribute会创建一个属性,名称由您指定(@ModelAttribute("Testing") Test test)在给定的示例中,Test是bean,test是对bean的引用,Testing将可用于模型中,以便您可以进一步在jsp页面上使用它来检索存储在@ModelAttribute中的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接