SpringMVC--二、SpringMVC请求参数绑定和常用注解

请求参数绑定

绑定机制

我们都知道,表单中请求参数都是基于key=value的。 SpringMVC绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。

支持的数据类型

基本类型参数: 包括基本类型和String类型

我们在表单中的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)

index.jsp文件

1
2
3
4
5
6
<form method="post" action="param/test">
姓名:<input type="text" name="name"><br>
密码:<input type="text" name="password"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>

Controller类

1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/test")
public String ParamTest(String name, User user){
System.out.println(name + '\t' + user);
return "success";
}
}

User类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class User implements Serializable {
String password;
Integer age;
@Override
public String toString() {
return "User{" +
"password='" + password + '\'' +
", age=" + age +
'}';
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}

这里直接使用实体类的时候,表单中的name命名可以当作基本类型,但是当实体类中使用其他实体类,要使用如下命名方式

POJO类型参数: 包括实体类,以及关联的实体类

中文释义为简单的java对象,其实就是Java bean,好像是为了避免和EJB混淆所创造的简称
要求表单中参数名称和POJO类的属性名称保持一致。并且控制器方法的参数类型是POJO类型。例如:name.firstname
用于这里的实体类都要实现Serializable接口

index.jsp

1
2
3
4
5
6
7
<form method="post" action="param/test">
姓:<input type="text" name="name.firstname"><br>
名:<input type="text" name="name.secondname"><br>
密码:<input type="text" name="password"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>

Controller类

1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/test")
public String ParamTest(User user){
System.out.println(user);
return "success";
}
}

User类

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
public class User implements Serializable {
String password;
Name name;//自定义类型JavaBean
Integer age;

@Override
public String toString() {
return "User{" +
"password='" + password + '\'' +
", name=" + name +
", age=" + age +
'}';
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}

Name类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Name implements Serializable {
@Override
public String toString() {
return "Name{" +
"firstname='" + firstname + '\'' +
", secondname='" + secondname + '\'' +
'}';
}

private String firstname, secondname;

public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getSecondname() {
return secondname;
}
public void setSecondname(String secondname) {
this.secondname = secondname;
}
}

数组和集合类型参数: 包括List结构和Map结构的集合(包括数组)

  • 要求集合类型的请求参数必须在POJO中。在表单中请求参数名称要和POJO中集合属性名称相同。
    给List集合中的元素赋值,使用下标。 例如:list[0].user
    给Map集合中的元素赋值,使用键值对。例如:map[‘one’].user
  • 接收的请求参数是json格式数据。需要借助一个注解实现。

index.jsp

1
2
3
4
5
6
7
8
9
10
11
<form method="post" action="param/test">
姓一:<input type="text" name="nameList[0].firstname"><br>
名一:<input type="text" name="nameList[0].secondname"><br>
姓二:<input type="text" name="nameList[1].firstname"><br>
名二:<input type="text" name="nameList[1].secondname"><br>
姓三:<input type="text" name="nameMap['one'].firstname"><br>
名三:<input type="text" name="nameMap['one'].secondname"><br>
密码:<input type="text" name="password"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>

Controller类同上

User类

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
public class User implements Serializable {
String password;
Integer age;
List<Name> nameList;
Map<String, Name> nameMap;

@Override
public String toString() {
return "User{" +
"password='" + password + '\'' +
", age=" + age +
", nameList=" + nameList +
", nameMap=" + nameMap +
'}';
}
public List<Name> getNameList() {
return nameList;
}
public void setNameList(List<Name> nameList) {
this.nameList = nameList;
}
public Map<String, Name> getNameMap() {
return nameMap;
}
public void setNameMap(Map<String, Name> nameMap) {
this.nameMap = nameMap;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}

Name类同上

请求参数乱码问题

在web.xml中配置Spring提供的过滤器类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 配置过滤器,解决中文乱码的问题 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filterclass>
<!-- 指定字符集 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!--/*代表拦截所有-->
<url-pattern>/*</url-pattern>
</filter-mapping>

自定义类型转换器

任何变量从jsp传到控制器用的都是字符串,当其到控制器时,Spring有自定义的类型转换器将其转换为特定的类型的变量,当有些变量无法通过Spring自定义类型转换器转换类型时,我们可以自己定义一个类型转换器来帮助转换。

例如Data类,默认日期格式为1999/10/1,当我们输入1999-10-1时则会发生错误,Spring自定义类型转换器无法转换,此时我们自己定义一个类型转换器,来讲1999-10-1格式的日期字符串转换成Data类。

定义一个类型转换器类

自定义的类型转换器类需要继承一个Converter接口,要看清楚是Spring核心包中的Converter接口:img

类中的两个泛型S和T分别写要从类型S转换到类型T

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class StringToData implements Converter<String, Date>{
@Override
public Date convert(String source) {
if (source==null){
throw new RuntimeException("值空");
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

try {
return dateFormat.parse(source);
}catch (Exception e){
throw new RuntimeException("错误");
}
}
}

配置自定义类型转换器

在resources中的xml配置文件中配置自定义类型转换器,就是你配置视图解析器的那个配置文件。

1
2
3
4
5
6
7
8
9
10
11
<bean id="conversionServiceFactoryBean"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.utils.StringToData"></bean>
</set>
</property>
</bean>

<!--配置spring开启注解mvc的支持-->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>

这里还要修改开启注解mvc的支持,因为在开启注解mvc的支持时是默认开启了处理器映射器、处理器适配器、视图解析器三大组件,而现在想用类型转换器,则必须在这里配置开启。

常用注解

RequestParam

使用说明

作用:把请求中的指定名称的参数传递给控制器中的形参赋值

属性:

  1. value:请求参数中的名称(当你使用了RequestParam注解之后就必须按照value中的命名来传递参数,不能再像请求参数绑定中,把参数命名成相同的名字了。)
  2. required:请求参数中是否必须提供此参数,默认值是true(即必须提供,如果不提供将报错)

示例

index.jsp
1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="anno/testRp?name=haha">testRequestParam</a>
</body>
</html>
Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
@RequestMapping("anno")
public class AnnoController {
@RequestMapping("/testRp")
public String testRp(@RequestParam(value = "name", required = false) String username){
if (username==null)
System.out.println("无用户名");
else
System.out.println(username);

return "success";
}
}

在这个例子中我们把required改为false,则当没有参数传递给username时它为null。当使用RequestParam时则必须按照它的value在jsp页面命名,不能像请求参数绑定中命名成相同的名字。例如在这个例子中,jsp页面想要传递参数给控制器中的username,就必须给控制器传递一个名为name的参数,而不能传递一个名为username的参数。

RequestBody

使用说明

作用:用于获取请求体的内容,直接使用得到是key=value&key=value…结构的数据。(注意:get方法不可以,因为get没有请求体,它把参数都封装到地址栏了)

属性:

  1. required:是否必须有请求体,默认值是true。当取值为true时,get请求方式会报错。如果取值为false,get请求得到是null。

示例

index.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form method="post" action="anno/testRequestBody">
用户名:<input type="text" name="username"><br>
密码:<input type="text" name="password"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
Controller
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("anno")
public class AnnoController {
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String body){
System.out.println(body);
return "success";
}
}

在本例中的请求体输出示例:username=int64Saturday&password=111&age=111

PathVaribale

使用说明

作用:用于绑定url中的占位符。例如:请求url中 /delete/{id},这个{id}就是url占位符。
url支持占位符是spring3.0之后加入的。是springmvc支持rest风格URL的一个重要标志。

那么什么时rest风格的URL呢,一般rest风格都称为restful风格,它其实就是一种规范,区别于我们平时普通的url,见下图

img

(图中的FindById()方法少写了个参数id)从图中可看出普通的url每个方法都会对应一个地址,但是restful风格的url相同类下的每个方法地址都是相同的。restful风格的url是通过http协议的四种方法来寻找要执行的方法,即:

  1. GET 用来获取资源
  2. POST 用来新建资源
  3. PUT 用来更新资源
  4. DELETE 用来删除资源。

但是当遇到相同的方法时,如上图的FindAll()方法和FindById(id)方法,它会通过不同的参数来寻找特定的方法,有些类似函数的重载。上图中的{id}是占位符,当我们想调用FindById(id)方法时,参数id=10,此时的url应该为:/user/10

但是form表单只支持GET和POST方法,而DELETE、PUT等方法并不支持,Spring3.0添加了一个过滤器能帮我们模拟这些方法,但是配置比较麻烦,不如其他的一些类实现容易,这里就不赘述了,想了解的可以自行百度。

属性:

  1. value:用于指定url中占位符名称。
  2. required:是否必须提供占位符。

示例

index.jsp
1
2
3
4
5
6
7
8
<body>
<form method="post" action="anno/10">
用户名:<input type="text" name="username"><br>
密码:<input type="text" name="password"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
</body>
Controller
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("anno")
public class AnnoController {
@RequestMapping("/{sid}")
public String testPathVaribale(@PathVariable(value = "sid") String id){
System.out.println(id);
return "success";
}
}

最后控制台输出结果为10。

RequestHeader

使用说明

作用:用于获取请求消息头。http请求头有很多种,详情见百度百科:http请求头

属性:

  1. value:提供消息头名称
  2. required:是否必须有此消息头

注: 在实际开发中一般不怎么用。

示例

index.jsp
1
<a href="anno/testRequestHeader">testRequestHeader</a>
Controller
1
2
3
4
5
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader(value = "Accept") String header){
System.out.println(header);
return "success";
}

CookieValue

使用说明

作用:用于把指定 cookie 名称的值传入控制器方法参数。

属性:

  1. value:指定 cookie 的名称。
  2. required:是否必须有此 cookie。

注: 在实际开发中一般不怎么用。

示例

index.jsp
1
<a href="anno/testCookieValue">testRequestHeader</a>
Controller
1
2
3
4
5
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue(value = "JSESSIONID") String cookieValue){
System.out.println(cookieValue);
return "success";
}

ModelAttribute

使用说明

作用:该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。

  1. 出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
  2. 出现在参数上,获取指定的数据给参数赋值。

属性:

  1. value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。

应用场景:

当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。

示例1

index.jsp
1
2
3
4
5
6
<form method="post" action="anno/testModelAttribute">
用户名:<input type="text" name="username"><br>
密码:<input type="text" name="password"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
Controller
1
2
3
4
5
6
7
8
9
10
@RequestMapping("/testModelAttribute")
public String testModelAttribute (User user){
System.out.println("testModelAttribute执行了..." + user.getUsername());
return "success";
}

@ModelAttribute
public void showUser(){
System.out.println("showUser执行了...");
}

最后控制台输出结果为:
showUser执行了…
testModelAttribute执行了…int64Saturday

示例2

有返回值,表单中没有输入age的文本框,如果不进行配置,则age为null

index.jsp
1
2
3
4
5
<form method="post" action="anno/testModelAttribute">
用户名:<input type="text" name="username"><br>
密码:<input type="text" name="password"><br>
<input type="submit" value="提交">
</form>
Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RequestMapping("/testModelAttribute")
public String testModelAttribute (User user){
System.out.println("testModelAttribute执行了...");
System.out.println(user);
return "success";
}

@ModelAttribute
public User showUser(String username){
User user = new User();
//模拟数据库查询
user.setAge(10);
user.setPassword("111");
user.setUsername(username);
System.out.println("showUser执行了...");
return user;
}

示例3

无返回值,没有返回值时,需要方法提供一个map结构,将User对象存入map中,然后在控制器方法中用ModelAttribute修饰形参从map中取出该对象。

index.jsp

同上

Controller
1
2
3
4
5
6
7
8
9
10
@ModelAttribute
public void showUser(String username, Map<String, User> map){
User user = new User();
//模拟数据库查询
user.setAge(10);
user.setPassword("111");
user.setUsername(username);
map.put("user", user);
System.out.println("showUser执行了...");
}

SessionAttribute

使用说明

作用:用于多次执行控制器方法间的参数共享,作用于类上。

属性:

  1. value:用于指定存入的属性名称
  2. type:用于指定存入的数据类型

示例

index.jsp
1
2
3
<a href="anno/testSaveSessionAttribute">SaveSessionAttribute</a><br>
<a href="anno/testGetSessionAttribute">GetSessionAttribute</a><br>
<a href="anno/testDeleteSessionAttribute">DeleteSessionAttribute</a>
Controller
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
@Controller
@RequestMapping("anno")
@SessionAttributes(value = {"username", "password"})
public class AnnoController {
@RequestMapping("/testSaveSessionAttribute")
public String testSaveSessionAttribute (Model model){
System.out.println("向session中保存了数据");
model.addAttribute("username", "root");
model.addAttribute("password", "1234");
return "success";
}
@RequestMapping("/testGetSessionAttribute")
public String testGetSessionAttribute (ModelMap modelMap){
System.out.println("从session中获取了数据");
String username = (String)modelMap.get("username");
String password = (String)modelMap.get("password");
System.out.println("username:" + username + "\tpassword:" + password);
return "success";
}
@RequestMapping("/testDeleteSessionAttribute")
public String testDeleteSessionAttribute (SessionStatus sessionStatus){
System.out.println("从session中删除了数据");
//设置完成,就是这些操作做完了,它会帮你把session中的数据清除
sessionStatus.setComplete();
return "success";
}
}

超链接点击步骤:存值->取值->删除->取值
控制台输出:

向session中保存了数据
从session中获取了数据
username:root password:1234
从session中删除了数据
从session中获取了数据
username:null password:null

Donate comment here