3.12 @ModelAttribute注解

3.12 @ModelAttribute注解

用途:将请求参数绑定到对象

org.springframework.web.bind.annotation.ModelAttribute注解用于将请求参数绑定到Model对象中

属性

@ModelAttribute注解只支持一个属性value,类型为String,表示绑定的属性名称。

注意

**提示:**被@ModelAttribute注释的方法会在Controller每个方法执行前被执行,因此在个Controlller被映射到多个URL时,要谨慎使用。

@ModelAttribute注解的使用方式有很多种,下面来逐一介绍。

示例 @ModelAttribute注解的使用

项目结构

展开/折叠
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\ModelAttributeTest
├─src\
│ └─org\
│ └─fkit\
│ ├─controller\
│ │ ├─FormController.java
│ │ ├─ModelAttribute1Controller.java
│ │ ├─ModelAttribute2Controller.java
│ │ ├─ModelAttribute3Controller.java
│ │ ├─ModelAttribute4Controller.java
│ │ └─ModelAttribute5Controller.java
│ └─domain\
│ └─User.java
└─WebContent\
├─index.jsp
├─META-INF\
│ └─MANIFEST.MF
└─WEB-INF\
├─content\
│ ├─login4.jsp
│ ├─loginForm1.jsp
│ ├─loginForm2.jsp
│ ├─loginForm3.jsp
│ ├─loginForm4.jsp
│ ├─loginForm5.jsp
│ ├─result1.jsp
│ ├─result2.jsp
│ ├─result3.jsp
│ └─result5.jsp
├─lib\
│ ├─commons-logging-1.2.jar
│ ├─spring-aop-5.0.1.RELEASE.jar
│ ├─spring-aspects-5.0.1.RELEASE.jar
│ ├─spring-beans-5.0.1.RELEASE.jar
│ ├─spring-context-5.0.1.RELEASE.jar
│ ├─spring-context-indexer-5.0.1.RELEASE.jar
│ ├─spring-context-support-5.0.1.RELEASE.jar
│ ├─spring-core-5.0.1.RELEASE.jar
│ ├─spring-expression-5.0.1.RELEASE.jar
│ ├─spring-instrument-5.0.1.RELEASE.jar
│ ├─spring-jcl-5.0.1.RELEASE.jar
│ ├─spring-jdbc-5.0.1.RELEASE.jar
│ ├─spring-jms-5.0.1.RELEASE.jar
│ ├─spring-messaging-5.0.1.RELEASE.jar
│ ├─spring-orm-5.0.1.RELEASE.jar
│ ├─spring-oxm-5.0.1.RELEASE.jar
│ ├─spring-test-5.0.1.RELEASE.jar
│ ├─spring-tx-5.0.1.RELEASE.jar
│ ├─spring-web-5.0.1.RELEASE.jar
│ ├─spring-webflux-5.0.1.RELEASE.jar
│ ├─spring-webmvc-5.0.1.RELEASE.jar
│ └─spring-websocket-5.0.1.RELEASE.jar
├─springmvc-config.xml
└─web.xml

index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@ModelAttribute</title>
</head>
<body>
<h3>测试@ModelAttribute的不同用法</h3>
<a href="loginForm1">测试@ModelAttribute(value="")注释返回具体类的方法 </a>
<br>
<a href="loginForm2">测试@ModelAttribute注释的返回值为void的方法</a>
<br>
<a href="loginForm3">测试@ModelAttribute注释的返回值为具体类的方法</a>
<br>
<a href="loginForm4">测试@ModelAttribute@RequestMapping同时注释一个方法的情况</a>
<br>
<a href="loginForm5">测试@ModelAttribute注释一个方法的参数 </a>
<br>
</body>
</html>

FormController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.fkit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class FormController
{
// 该方法映射的请求为http://localhost:8080/ModelAttributeTest/{formName}
@RequestMapping(value = "/{formName}")
// @PathVariable: 取出URL中的动态内容{formName}赋值给其后面的形参
public String loginForm(@PathVariable String formName)
{
// 动态跳转页面
return formName;
}
}

1.测试@ModelAttribute(value=””)注释返回具体类的方法

loginForm1.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@ModelAttribute</title>
</head>
<body>
<h3>测试@ModelAttribute(value="")注释返回具体类的方法</h3>
<form action="login1" method="post">
<table>
<tr>
<td><label>登录名: </label></td>
<td><input type="text" id="loginname"
name="loginname"></td>
</tr>
<tr>
<td><input id="submit" type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>

ModelAttribute1Controller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.fkit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class ModelAttribute1Controller
{
// @ModelAttribute注解的方法会 先执行
// @ModelAttribute("loginname")表示在model之中设置一个名为loginname的属性,
// 属性的值是方法的返回值.
@ModelAttribute("loginname")
public String (@RequestParam("loginname") String loginname)
{
// 该返回值将作为model中loginname属性的值.
return loginname;
}
@RequestMapping(value = "/login1")
public String login1()
{
// 返回视图的路径
return "result1";
}
}

ModelAttributelController类中除了@RequestMapping映射的login1方法之外,还提供了一个userModel1方法,该方法上有一个@ModelAttribute注解。此处@ModelAttribute注解默认的value值为“loginname”,用来指定model属性的名称,而model属性的值就是userModel1方法的返回值。被@ModelAttribute注解的userMode11方法会先于login1调用,它把请求参数loginname的值赋给userModel1方法的形式参数loginname,并设置了一个属性loginnameModel当中,而属性的值就是方法的形式参数loginame的值。

result1.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@ModelAttribute(value="")注释返回具体类的方法</title>
</head>
<body>
访问request作用范围域中的loginname对象:${requestScope.loginname }
<br>
</body>
</html>

在跳转的result1.jsp中可以访问到由@ModelAttribute设置在Model中的的loginname的值。

测试

部署ModelAttributeTest这个Web应用,在浏览器中输入如下URL来测试应用:

1
http://localhost:8080/ModelAttributeTest/index.jsp

单击”测试@ModelAttribute(value="")注释返回具体类的方法“超链接发送请求,跳转到loginForm1.jsp页面.
输入登录名"test",单击”登录“按钮发送请求,而后将先调用userMode1方法,再调用login1方法并跳转到result1.jsp页面。

可以看到,在request作用域中访问到了Model的值。

2.测试@ModelAttribute注释void返回值的方法

loginForm2.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@ModelAttribute</title>
</head>
<body>
<h3>测试@ModelAttribute注释void返回值的方法</h3>
<form action="login2" method="post">
<table>
<tr>
<td><label>登录名: </label></td>
<td><input type="text" id="loginname"
name="loginname"></td>
</tr>
<tr>
<td><label>密码: </label></td>
<td><input type="password" id="password"
name="password"></td>
</tr>
<tr>
<td><input id="submit" type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>

ModelAttribute2Controller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package org.fkit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class ModelAttribute2Controller
{
// model属性名称和model属性对象由model.addAttribute()实现,前提是要在方法中加入一个Model类型的参数。
// 注意:当URL或者post中不包含对应的参数时,程序会抛出异常。
@ModelAttribute
public void userModel2(@RequestParam("loginname") String loginname,
@RequestParam("password") String password, Model model)
{
model.addAttribute("loginname", loginname);
model.addAttribute("password", password);
}
@RequestMapping(value = "/login2")
public String login2()
{
// 显示结果
return "result2";
}
}

ModelAttribute2Controller类中除了@RequestMapping映射的login2方法之外,还提供了一个userModel2方法,该方法上有一个@ModelAttribute注解。 userMode12方法会先于login方法被调用,它把请求参数值赋给对应变量,model属性名称和值由model.addAttribute()方法实现,前提是要在方法中加入一个Model类型的参数

result2.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@ModelAttribute注释void返回值的方法</title>
</head>
<body>
访问request作用范围域中的loginname对象:${requestScope.loginname }
<br> 访问request作用范围域中的password对象:${requestScope.password }
</body>
</html>

在跳转的result2.jsp中可以访问到由@ModelAttribute注解设置的loginnamepassword的值.

测试

在浏览器中输入如下URL来测试应用

1
http://localhost:8080/ModelAttributeTest/index.jsp

单击“测试@ModelAttribute注释void返回值的方法”超链接发送请求,将会跳转到loginForm2.jsp页面。
输入登录名"test",密码“123456”,单击“登录”按钮发送请求,而后将先调用userModel2方法,再调用login2方法,并跳转到result2.jsp页面.
可以看到,在request作用域中访问到了Model的值。

3.测试@ModelAttribute注释返回具体类的方法

ModelAttribute3Controller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package org.fkit.controller;

import java.util.ArrayList;
import java.util.List;
import org.fkit.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class ModelAttribute3Controller
{
// 静态List<User>集合,此处代替数据库用来保存注册的用户信息
private static List<User> userList;
// UserController类的构造器,初始化List<User>集合
public ModelAttribute3Controller()
{
super();
userList = new ArrayList<User>();
User user1 = new User("test", "123456", "测试用户");
User user2 = new User("admin", "123456", "管理员");
// 存储User用户,用于模拟数据库数据
userList.add(user1);
userList.add(user2);
}
// 根据登录名和密码查询用户,用户存在返回包含用户信息的User对象,不存在返回null
public User find(String loginname, String password)
{
for (User user : userList)
{
if (user.getLoginname().equals(loginname)
&& user.getPassword().equals(password))
{
return user;
}
}
return null;
}
// @ModelAttribute方法会先执行
// model属性的名称没有指定,它由方法的返回类型隐含表示
// 如这个方法的返回值类型是User,那么这个model属性的名称是user。
// model的返回值是方法生成的User对象.
@ModelAttribute
public User userModel3(@RequestParam("loginname") String loginname,
@RequestParam("password") String password)
{
return find(loginname, password);
}
@RequestMapping(value = "/login3")
public String login3()
{
return "result3";
}
}

ModelAttribute3Controller类中除了@RequestMapping映射的login3方法之外,还提供了一个userModel3方法,该方法上有一个@ModelAttribute注解。 userModel3方法会先于login3方法被调用,这里model属性的名称没有被指定,它由@ModelAttribute注解的userModel3方法的返回类型隐含表示,如这个方法返回User类型,那么这个model属性的名称就是user。此处find(loginname, password)方法用来模拟数据库根据登录名和密码查询用户的功能实现。

result3.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@ModelAttribute注释返回具体类的方法</title>
</head>
<body>
访问request作用范围域中的user对象:${requestScope.user.username }
<br>
</body>
</html>

在跳转的result2.jsp页面中可以访问到由@ModelAttribute设置的loginnamepassword的值。

测试

在浏览器中输入如下URL来测试应用

1
http://localhost:8080/ModelAttributeTest/index.jsp

单击”测试@ModelAttribute注释返回具体类的方法“超链接发送请求,将跳转到lσginForm3.jsp页面,
输入登录名"test",密码"123456",单击”登录”按钮发送请求,而后将先调用userModel3方法,再调用login3方法,并跳转到result3.jsp页面.
可以看到,在request作用域中访问到了User对象。

4 测试ModelAttribute和@RequestMapping同时注释一个方法

ModelAttribute4Controller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.fkit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class ModelAttribute4Controller
{
// 这时这个方法的返回值并不是表示一个视图名称,而是model属性的值.
// 视图名称是@RequestMapping的value值
// Model属性名称由@ModelAttribute(value="")指定,
// 相当于在request中封装了username(key)=admin(value)。
@RequestMapping(value = "/login4")
@ModelAttribute(value = "username")
public String login4()
{
// 返回返回model之中username属性的值.
return "admin";
}
}

ModelAttribute4Controller中,@ModelAttribute@RequestMapping同时注释一个方法,此时

  • login4方法的返回值并不是一个视图名称,而是model属性的值,视图名称是@RequestMappingvalue"/login4",Model的属性名称由@ModelAttributevalue值指定,这相当于在request中封装了username(key)= admin(value)
  • 注意,
  • 此处login4方法跳转的结果是"/login4"

login4.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@ModelAttribute@RequestMapping同时注释一个方法</title>
</head>
<body>
访问request作用范围域中的username对象:${requestScope.username }
<br>
</body>
</html>

测试

在浏览器中输入如下URL来测试应用

1
http://localhost:8080/ModelAttributeTest/index.jsp
  • 单击“测试@ModelAttribute和@RequestMapping同时注释一个方法”超链接发送请求,而后跳转到loginForm4.jsp页面
  • 输入登录名“test”,密码“123456”,单击”登录”按钮发送请求,将调用login4方法,跳转到login4.jsp页面,如图3.18所示。
  • 可以看到,在request作用域中访问到了username的值,也就是login4方法的返回值"admin"

5 测试@ModelAttribute注释一个方法的参数

ModelAttribute5Controller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.fkit.controller;

import org.fkit.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class ModelAttribute5Controller
{
// @ModelAttribute注释方法参数,
// 会把表单name属性为loginname的值赋值给User的loginname属性
// 会把表单name属性为password的值赋值给User的password属性
@RequestMapping(value = "/login5")
public String login5(@ModelAttribute User user)
{
user.setUsername("管理员");
// 返回的是视图
return "result5";
}
}

ModelAttribute5Controller类中login5方法的参数User使用了@ModelAttribute注解,前台页面的控件的值会自动入参@ModelAttribute注解修饰的对象 的同名属性当中。这种方式是在实际开发中使用最多的方式

result5.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@ModelAttribute注释一个方法的参数</title>
</head>
<body>
访问request作用范围域中的user对象的loginname属性:${requestScope.user.loginname }
<br>
访问request作用范围域中的user对象的password属性:${requestScope.user.password }
<br>
访问request作用范围域中的user对象的username属性:${requestScope.user.username }
<br>
</body>
</html>

测试

在浏览器中输入如下URL来测试应用

1
http://localhost:8080/ModelAttributeTest/index.jsp

单击测试@ModelAttribute注释一个方法的参数超链接发送请求,跳转到loginForm5.jsp页面.输入登录名test,密码123456,单击登录按钮发送请求,而后将调用login5方法,跳转到result5.jsp页面.
可以看到,在request作用域中访问到了User对象.

提示@ModeAttribute注解的使用方法有很多种,非常灵活,读者可以根据业务需求选择使用。