3.7 @RequestHeader注解

用途

org.springframework.web.bind.annotation.RequestHeader注解用于将请求的头信息数据映射到请求处理方法的形式参数

属性

使用@Requestheader注解可指定如下表所示的属性。

属性 类型 是否必要 说明
name String 指定请求参数绑定的名称
value String name属性的别名
required boolean 指示参数是否必须绑定
defaultValue String 如果没有传递参数而使用的默认值

示例代码

@RequestHeader注解示例代码如下:

1
2
3
4
5
6
7
@RequestMapping(value="/requestHeaderTest")
public void requestHeaderTest(
@RequestHeader ("User-Agent") String userAgent,
@RequestHeader (value="Accept") String[] accepts)
{
...
}

以上配置自动将请求头"User-Agent"的值赋到userAgent变量上,并将请求头"Accept"的值赋到accepts变量上。

示例:@RequestHeader注解的使用

新建一个项目RequestHeaderTest,加入所需的jar文件.

项目结构

展开/折叠
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
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\RequestHeaderTest
├─src\
│ └─org\
│ └─fkit\
│ └─controller\
│ └─RequestHeaderController.java
└─WebContent\
├─index.jsp
├─META-INF\
│ └─MANIFEST.MF
└─WEB-INF\
├─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

示例代码如下:

RequestHeaderController.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
package org.fkit.controller;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;

@Controller
public class RequestHeaderController
{
// 测试@RequestHeader注解
// 该方法映射的请求为 /requestHeaderTest
@GetMapping(value = "/requestHeaderTest")
// 把请求头User-Agent的内容赋值给方法的形式参数userAgent
public void requestHeaderTest(@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value = "Accept") String[] accepts,
HttpServletResponse response)
{
response.setContentType("text/html; charset=utf-8");
response.setCharacterEncoding("utf-8");
PrintWriter writer;
try
{
writer = response.getWriter();
writer.println(
"通过@requestHeaderTest获得User-Agent: " + userAgent + "<br>");
writer.println("通过@requestHeaderTest获得Accept: " + "<br>");
// 循环输出头信息
for (String accept : accepts)
{
writer.println(accept + "<br>");
}
} catch (IOException e)
{
e.printStackTrace();
}
}
}

requestHeaderTest方法中使用@RequestHeader注解获取请求头"User-Agent"的值并赋给userAgent变量,获取请求头"Accept"的值并赋给accepts变量。

index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>@RequestHeader测试</title>
</head>
<body>
<h2>@RequestHeader测试</h2>
<a href="requestHeaderTest">测试@RequestHeader注解</a>
<br>
</body>
</html>

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

1
http://localhost:8080/RequestHeaderTest/

此时会显示index.jsp页面的内容,单击测试@RequestHeader注解超链接发送请求,将调用requestHeaderTest方法,浏览器显示效果如下:

1
2
3
4
5
6
通过@requestHeaderTest获得User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
通过@requestHeaderTest获得Accept:
text/html
application/xhtml+xml
application/xml;q=0.9
*/*;q=0.8

可以看到,请求头User-Agent的值赋给了userAgent变量,请求头Accept的值赋给了accepts变量,不过要注意,不同的浏览器请求头数据略有不同。

示例 @CrossOrigin注解的使用

接下来测试跨域发送请求,再新建一个项目CrossOriginTest,加入所需的jar文件.

项目结构

展开/折叠
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
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\CrossOriginTest
├─src\
│ └─org\
│ └─fkit\
│ └─controller\
│ └─CrossOriginController.java
└─WebContent\
├─META-INF\
│ └─MANIFEST.MF
└─WEB-INF\
├─content\
│ └─welcome.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

CrossOriginController.java

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;

// 允许所有域发送过来的请求
@CrossOrigin(maxAge = 3600)
@Controller
public class CrossOriginController
{
// 只允许origins属性中指定的域的请求
@CrossOrigin(origins = "http://localhost:8080/VariableTest")
@GetMapping(value = "/welcome")
public String welcome()
{
System.out.println("处理跨域请求");
return "welcome";
}
}

CrossOriginController类和welcome方法上都使用了@CrossOrigin注解。 welcome方法接收到跨域请求进行简单处理后,跳转到welcome.jsp

welcome.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@CrossOrigin注解</title>
</head>
<body>
<br>恭喜您,测试跨域访问成功!
</body>
</html>

此外,还需要在web.xml文件中配置Spring MVC的前端控制器DispatcherServlet,因为每次配置基本一致,此处不再赘述,读者可自行配置。

部署测试

同时部署VariableTestCrossOriginTest两个Web应用,在浏览器中输入如下URL来测试进入VariableTest应用:

1
http://localhost:8080/VariableTest/

然后点击测试@CrossOrigin注解超链接:

1
2
<!-- 跨域请求 -->
<a href="http://localhost:8080/CrossOriginTest/welcome">测试@CrossOrigin注解</a>

向另一个Web应用CrossOriginTest发送跨域请求,CrossOriginTest应用的CrossOriginController控制器的welcome方法将会处理这个跨域请求,控制台输出结果如下:

1
处理跨域请求

同时浏览器上将显示CrossOriginTest应用的welcome.jsp页面。

总结

@CrossOrigin注解可以接收从另一个Web应用发来的跨域请求。

示例 @PathVariable注解和@MatrixVariable注解的使用

新建一个项目VariableTest,加入所需的jar文件:

项目结构

展开/折叠
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
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\VariableTest
├─src\
│ └─org\
│ └─fkit\
│ └─controller\
│ └─VariableController.java
└─WebContent\
├─index.jsp
├─META-INF\
│ └─MANIFEST.MF
└─WEB-INF\
├─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
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>处理请求URL注解测试</title>
</head>
<body>
<h2>处理请求URL注解测试</h2>
<a href="pathVariableTest/1">测试@PathVariable注解</a>
<br>
<a href="matrixVariableTest/1;name=jack;age=23">测试@MatrixVariable注解</a>
<br>
<a href="productTest/computer;brand=apple,acer;low=2000;height=10000">商品条件查询(品牌,价格区间)</a>
<br>
<!-- 跨域请求 -->
<a href="http://localhost:8080/CrossOriginTest/welcome">测试@CrossOrigin注解</a>
<br>
</body>
</html>

VariableController.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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package org.fkit.controller;

import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.PathVariable;

@Controller
public class VariableController
{

// 测试@PathVariable注解
// 该方法映射的请求为/VariableTest/pathVariableTest/1
@GetMapping(value = "/pathVariableTest/{userId}")
public void pathVariableTest(@PathVariable Integer userId,
HttpServletResponse response)
{
// 直接响应不跳转页面
try
{
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
response.getWriter().write("通过@PathVariable获得数据: userId=" + userId);
} catch (IOException e)
{
e.printStackTrace();
}
}

// 测试@MatrixVariable注解
// 该方法映射的请求为/VariableTest/matrixVariableTest/1;name=jack;age=23
@GetMapping(value = "/matrixVariableTest/{userId}")
public void matrixVariableTest(@PathVariable Integer userId,
@MatrixVariable(value = "name", pathVar = "userId") String name,
@MatrixVariable(value = "age", pathVar = "userId") Integer age,
HttpServletResponse response)
{
// 直接响应不跳转页面
try
{
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
response.getWriter().write("通过@PathVariable获得数据: userId=" + userId);
response.getWriter().write(
"通过@MatrixVariable获得数据: name=" + name + " age=" + age);
} catch (IOException e)
{
e.printStackTrace();
}
}

// 测试@MatrixVariable注解的复杂例子
// 该方法映射的请求为//VariableTest/productTest/computer;brand=apple,acer;low=2000;height=10000
@GetMapping(value = "/productTest/{goods}")
public void productTest(@PathVariable String goods,
@MatrixVariable(value = "brand", pathVar = "goods") List<String> brand,
@MatrixVariable(value = "low", pathVar = "goods") Integer low,
@MatrixVariable(value = "height", pathVar = "goods") Integer height,
HttpServletResponse response)
{
// 直接响应不跳转页面
try
{
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
response.getWriter().println("通过@PathVariable获得数据: goods=" + goods+"<br>");
response.getWriter().println("通过@MatrixVariable获得数据:brand=" + brand+"<br>");
response.getWriter().println(
"通过@MatrixVariable获得数据: low=" + low + " height=" + height+"<br>");
} catch (IOException e)
{
e.printStackTrace();
}
}
}

springmvc-config.xml

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- spring可以自动去扫描base-pack下面的包或者子包下面的java文件, -->
<!-- 如果扫描到有Spring的相关注解的类,则把这些类注册为Spring的bean -->
<context:component-scan
base-package="org.fkit.controller" />
<!-- 默认装配方案 -->
<!-- @MatrixVariable注解功能在SpringMVC中默认是不启用的 -->
<!-- 启用它需要设置enable-matrix-variables="true" -->
<mvc:annotation-driven
enable-matrix-variables="true" />
<!-- 静态资源处理 -->
<mvc:default-servlet-handler />

<!-- 视图解析器 p:prefix属性表示前缀 p:suffix 表示后缀 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/content/" p:suffix=".jsp" />

</beans>

此外,还需要在web.xml文件中配置Spring MVC的前端控制器DispatcherServlet,因为每次配置基本相同此处不再赘述,读者可自行配置。
部署VariableTest这个Web应用,在浏览器中输入如下URL来测试应用:

1
http://localhost:8080/VariableTest/

此时Spring MVC会跳转到index.jsp

测试

测试@PathVariable注解

VariableController类的pathVariableTest方法用于测试@PathVariable注解,它会将请求路径/pathVariableTest/1"userId的值“1”赋给方法参数的userId变量。单击”测试@PathVariable注解“超链接发送请求,将调用pathVariableTest方法,此时浏览器显示如下文字:

1
通过@PathVariable获得数据: userId=1

测试@MatrixVariable注解

VariableController类的matrixVariableTest方法用于测试@MatrixVariable注解,它会将请求路径"/matrixVariableTest/1;name=jack;age=23"中的name参数的值"jack"赋给方法形式参数name,将age参数的值23赋给方法参数的形式参数age。单击“测试@Matrixvariable注解”超链接发送请求,将调用matrixVariableTest方法.此时浏览器显示的如下文字:

1
通过@PathVariable获得数据: userId=1通过@MatrixVariable获得数据: name=jack age=23

可以看到,<a href=" matrixVariableTest/1;name=jack;age=23">测试MatrixVariable注解</a>的参数name的值"jack"被传递到方法形式参数name,参数age的值“23”被传递到方法的age变量,并显示在浏览器上。

多条件组合查询

MatrixVariable注解还可以完成复杂的参数注入,非常方便地进行多条件组合查询。本例以商品查询为例,详细介绍MatrixVariable的使用。
VariableController类的productTest方法用于商品条件查询,传递的参数包括商品品牌价格区间,它会将请求路径"/productTest/computer;brand=apple,acer;low=2000;height=10000"之中的:

  • brand参数的值"apple,acer"赋给方法参数的brand变量,该变量是一个List集合;
  • low参数的值"2000"赋给方法参数的low变量;
  • height参数的值"10000"赋给方法参数的height变量。

该请求表示一个商品的条件组合查询,商品名称是computer,查询的品牌是appleacer,价格区间是从200010000

单击“商品条件査询(品牌,价格区间)”超链接发送请求,将调用productTest方,浏览器显示结果如下:

1
2
3
通过@PathVariable获得数据: goods=computer
通过@MatrixVariable获得数据:brand=[apple, acer]
通过@MatrixVariable获得数据: low=2000 height=10000

可以看到,<a href="productTest/computer;brand=apple,acer;low=2000;height=10000">商品条件查询(品牌,价格区间)</a>的:

  • 参数brand的值"apple,acer"被传递到方法的brand集合变量,
  • 参数low的值"2000"被传递到方法的low变量参数
  • 参数height的值"10000"被传递到方法的height变量,最后输出在浏览器上

3.6 @CrossOrigin注解

用途:处理跨域请求

org.springframework.web.bind.annotation.Crossorigin注解用于在Spring MVC处理跨域请求

属性

使用@CrossOrigin注解可指定如下表所示的属性。

属性 类型 是否必要 说明
allowCredentials String 包含与请求的域相关的Cookie,使用时必须指定具体的域
allowedHeaders String数组 请求头中的请求列表
exposedHeaders String数组 客户端允许访问的响应头列表
maxAge long 响应前的缓存最大有效时间,单位是秒
methods RequestMethod数组 请求支持的方法,默认支持RequestMapping中设置的方法
origins Sting数组 所有支持域的集合,如果没有定义,默认支持所有域
value String数组 origins属性一样

示例代码

@CrossOrigin注解示例代码如下:

可以处理所有域的请求

代码:

1
2
3
4
5
6
@CrossOrigin(maxAge=3600)
@Controller
public class CrossoriginController
{
...
}

表示CrossoriginController控制器的所有方法可以处理所有域上的请求.

可以处理给定域的请求

代码:

1
2
3
4
5
6
7
8
9
@CrossOrigin(
origins="http://www.fkit.org",
maxAge=3600
)
@Controller
public class CrossOriginController
{
....
}

表示CrossOriginController控制器的所有方法可以处理http://www.fkit.org域上的请求。

@Crossorigin注解使用在类和方法上

下面的例子中,CrossoriginController类上有@Crossorigin注解,login方法上也有@CrossOrigin注解, Spring MVC合并两个注解的属性一起使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
//该控制器接受所有域上发过来的请求
@CrossOrigin(maxAge=3600)
@Controller
public class CrossoriginController
{
//该方法只接受这个`http://www.xxx.com`域发送来的请求
@CrossOrigin(origins="http://www.xxx.com")
@GetMapping(value="/login")
public String login()
{
....
}
}

3.5 @MatrixVariable注解

作用:多条件组合查询

org.springframework.web.bind.annotation.MatrixVariable注解拓展了URL请求地址的功能。使用@Matrixvariable注解时多个变量可以使用;(分号)分隔,该注解允许开发者进行多条件组合査询。

属性

使用@MatrixVariable注解可指定如下表所示的属性。

属性 类型 是否必要 说明
name String 指定请求参数绑定的名称,如果省略则绑定同名参数
value String name属性的别名
pathVar String matrix variable所在路径的url path变量的名称
required boolean 指示参数是否必须绑定
defaultValue String 如果没有传递参数而使用的默认值

示例代码

@MatrixVariable注解示例代码如下:

1
2
3
4
5
6
7
8
9
// 该方法映射的请求为/VariableTest/matrixVariableTest/1;name=jack;age=23
@GetMapping(value = "/matrixVariableTest/{userId}")
public void matrixVariableTest(@PathVariable Integer userId,
@MatrixVariable(value = "name", pathVar = "userId") String name,
@MatrixVariable(value = "age", pathVar = "userId") Integer age)
{
System.out.println("通过@PathVariable获得数据: userId=" + userId);
System.out.println("通过@MatrixVariable获得数据: name=" + name + " age=" + age);
}

假如请求的URLhttp://localhost:8080/VariableTest/MatrixVariableTest/1;name=jack;age=23,

  • 则自动将URL中模板变量{userId}绑定到通过@Pathvariable注解的同名形式参数上,即方法的形式参数userId将被赋值为1;
  • 通过@MatrixVariable注解绑定了形式参数nameage,即形式参数name将被赋值为jack,形式参数age将被赋值为23

启用@MatrixVariable注解功能

需要注意的是,@MatrixVariable注解功能在Spring MVC中默认是不启用的,启用它需要在Spring MVC配置文件的mvc:annotation-driven标签上设置enable-matrix-variables="true属性,示例代码如下:

1
2
3
4
5
<!-- 默认装配方案 -->
<!-- @MatrixVariable注解功能在SpringMVC中默认是不启用的 -->
<!-- 启用它需要设置enable-matrix-variables="true" -->
<mvc:annotation-driven
enable-matrix-variables="true" />

3.4 @PathVariable注解

处理 请求URL 部分的注解:@PathVariable@MatrixVariable@CrossOrigin

用途:获取请求URL中的动态参数

org.springframework.web.bind.annotation.PathVariable注解可以非常方便地获得请求URL中的动态参数

属性

使用@PathVariable注解可指定如下表所示的属性。

属性 类型 是否必要 说明
name String 指定请求参数绑定的名称,如果省略则绑定同名参数
value String name属性性的别名
required boolean 指示参数是否必须绑定

示例代码

@PathVariable注解示例代码如下:

1
2
3
4
5
@RequestMapping(value="/pathVariableTest/{userId}")
public void pathVariableTest(@PathVariable Integer userId)
{
......
}

假如请求的URLhttp://localhost:8080/VariableTest/PathVariableTest/1则自动将URL中模板变量{userId}绑定到通过@PathVariable注解的同名形式参数上,即方法的形式参数变量userId将被赋值为1.

3.3 @RequestParam注解

@RequestParam注解用途

org.springframework.web.bind.annotation.RequestParam注解用于**将指定的请求参数赋值给方法中的形参**。

@RequestParam注解属性

使用@RequestParam注解可指定如下表所示的属性。

属性 类型 是否必要 说明
name String 指定请求参数绑定的名称
value String name属性的别名
required boolean 指定参数是否必须绑定
dafaultValue String 如果没有传递参数而使用的默认值

请求方法的参数可以为 基本类型或String

请求处理方法参数的可选类型为Java基本数据类型String
示例代码如下:

1
2
3
4
5
6
7
@RequestMapping(value="/login")
public ModelAndView login(
@RequestParam("loginname") String loginname,
@RequestParam("password") String password
){
return ......;
}

假设请求如下:

1
http://localhost:8080/context/login?loginname=jack&password=123456

以上代码会将请求中的loginname参数的值"jack"赋给loginname变量, password参数的值"123456"赋给password变量。
需要注意的是,如果请求中不包含"loginname"参数,则将产生异常!因此,如果不能保证存在"loginname"参数,建议使用:
@RequestParam(value="loginname", required= false)
@RequestParam注解还有如下写法:
@RequestParam(value="loginname", required=true, defaultValue="admin");
其中required参数默认值为true,所以这里可以省略required=true这个参数。

示例 @RequestMapping和@RequestParam注解的使用

新建一个项目RequestMappingTest,加入所需的jar文件。

项目结构

展开/折叠
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
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\RequestMappingTest
├─src\
│ └─org\
│ └─fkit\
│ ├─controller\
│ │ └─UserController.java
│ └─domain\
│ └─User.java
└─WebContent\
├─META-INF\
│ └─MANIFEST.MF
└─WEB-INF\
├─content\
│ ├─loginForm.jsp
│ ├─registerForm.jsp
│ └─welcome.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

示例代码如下:

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.fkit.domain;
import java.io.Serializable;
// 域对象,实现序列化接口
public class User implements Serializable
{
private static final long serialVersionUID = 1L;
// 私有字段
private String loginname;
private String password;
private String username;
// 公共构造器
public User()
{
super();
}
// set/get方法
// 此处省略getter和setter方法,请自己补上
}

User是一个域对象,用来接收并封装从前台页面传递过来的数据。

UserController.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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
// RequestMapping可以用来注释一个控制器类,此时,所有方法都将映射为相对于类级别的请求,
// 表示该控制器处理所有的请求都被映射到 value属性所指示的路径下
@RequestMapping(value = "/user")
public class UserController
{

// 静态List<User>集合,此处代替数据库用来保存注册的用户信息
private static List<User> userList;

// UserController类的构造器,初始化List<User>集合
public UserController()
{
super();
userList = new ArrayList<User>();
}

// 该方法映射的请求为http://localhost:8080/context/user/register,该方法支持GET请求
@GetMapping(value = "/register")
public String registerForm()
{
System.out.println("register GET方法被调用...");
// 跳转到注册页面
return "registerForm";
}

// 该方法映射的请求为http://localhost:8080/RequestMappingTest/user/register,该方法支持POST请求
@PostMapping(value = "/register")
// 将请求中的loginname参数的值赋给loginname变量,password和username同样处理
public String register(@RequestParam("loginname") String loginname,
@RequestParam("password") String password,
@RequestParam("username") String username)
{
System.out.println("register POST方法被调用...");
// 创建User对象
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
user.setUsername(username);
// 模拟数据库存储User信息
userList.add(user);
// 跳转到登录页面
return "loginForm";
}

// 该方法映射的请求为http://localhost:8080/RequestMappingTest/user/login
@RequestMapping("/login")
public String login(
// 将请求中的loginname参数的值赋给loginname变量,password同样处理
@RequestParam("loginname") String loginname,
@RequestParam("password") String password,
Model model)
{
System.out.println("登录名:" + loginname + " 密码:" + password);
// 到集合中查找用户是否存在,此处用来模拟数据库验证
for (User user : userList)
{
if (user.getLoginname().equals(loginname)
&& user.getPassword().equals(password))
{
model.addAttribute("user", user);
return "welcome";
}
}
return "loginForm";
}
}

UserControlller类的代码解释如下:

  1. UserController类使用了@Controlller注解,是一个控制器类。
  2. UserControlller类上面使用了@RequestMapping(value="/user")注解,表示该控制器处理的所有请求都被映射到/user路径下。
  3. 本例没有使用数据库存储用户注册信息,所以定义了一个静态的List集合userList用来代替数据库存储用户数据。
  4. registerForm方法使用了@GetMapping(value="/register")注解,表示该方法映射的请求为:
    http://localhost:8080/RequestMappingTest/user/register,并且只支持GET请求。
    该方法返回字符串"registerForm",参考springmvc-config.xml中的视图解析器的配置信息,可以知道该方法只是跳转到registerForm.jsp注册页面
  5. register方法使用了@PostMapping(value="/register")注解,表示该方法映射的请求为:
    http://localhost:8080/RequestMappingTest/user/register并且只支持POST请求。
    该方法使用@RequestParam注解将指定的请求参数赋值给方法中的形参,之后创建了一个User对象保存用户传递的注册信息,最后将User对象存储到userList集合中,然后登录页面就可以到userList集合中进行用户登录业务逻辑的判断。该方法返回字符串loginForm,这样会跳转到loginForm.jsp登录页面。
    • 提示:registerForm方法和register方法虽然映射的请求一样,但是registerForm方法支持的是GET请求,而register方法支持的是POST请求。
  6. login方法使用了@RequestMapping("/login")注解,表示该方法映射的请求为:
    http://localhost/RequestMappingTest/user/login,这里没有设置method属性,表示支持所有方式的请求。该方法也使用@RequestParam注解将指定的请求参数赋值给方法中的形参。之后到集合中査找用户是否存在,此处用来模拟数据库验证。login方法中还有一个参数Model对象,调用该对象的addAttribute方法可以将数据添加到request当中。最后,如果用户登录成功则返回字符串welcome,并跳转到welcome.jsp欢迎页面;登录失败则返回字符串 loginForm,并跳转到loginForm.jsp登录页面.

registerForm.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
30
31
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>注册页面</title>
</head>
<body>
<h3>注册页面</h3>
<form action="register" 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><label>真实姓名: </label></td>
<td><input type="text" id="username" name="username"></td>
</tr>
<tr>
<td><input id="submit" type="submit" value="注册"></td>
</tr>
</table>
</form>
</body>
</html>

registerForm.jsp是一个注册页面,用户可以在这里输入登录名、密码和真实姓名,该表单被提交到register请求。注意,这里使用的是POST方式,响应请求的是UserController类的register方法。

loginForm.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
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录页面</title>
</head>
<body>
<h3>登录页面</h3>
<form action="login" 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>

lognForm.jsp是一个登录页面,用户可以在这里输入登录名和密码,该表单被提交login请求。这里使用的是POST方式,响应请求的是UserControlller类的login方法。

welcome.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@RequestMapping注解</title>
</head>
<body>
<!-- 页面可以访问Controller传递传递出来的模型User对象 -->
欢迎[${requestScope.user.username }]登陆
<br>
</body>
</html>

welcome.jsp是一个欢迎页面,用户登录成功后跳转到该页面,该页面使用了EL表达式访问request当中的user对象的username属性
此外,还需要在web.xml文件中配置Spring MVC的前端控制器DispatcherServlet,因为每次配置基本相同,此处不再赘述,读者可自行配置.
同时Spring MVC还需要springmvc-config.xml配置文件,ControllerTest项目中的springmvc-config.xml文件内容一致.读者可以自行配置。

测试

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

1
http://localhost:8080/RequestMappingTest/user/register
  • 此时可以在浏览器上看到注册页面,表示Spring MVC成功跳转到注册页面registerForm.jsp.
  • 输入登录名"test",密码"123456",真实姓名"测试用户",单击"注册"按钮。请求将会被提交到UserControlller类的register方法进行注册,注册的用户信息会被保存到UserController类的userList静态集合中。若注册成功,将会跳转到登录页面
  • 输入登录名"test",密码"123456",单击"登录"按钮。请求将会被提交到UserController类的login方法进行登录验证,若验证成功,将会跳转到欢迎页面.

3.2.6 页面转发

JSP中页面转发有两种情况:服务器内部跳转(forward)和客户端重定向(redirect)。 Spring MVC选择页面转发的方式也非常简单。

1. 转发到JSP页面

请求处理方法返回值是字符串的情况

服务器内部跳转

若请求处理方法返回字符串,默认使用服务器内部跳转( forward)。示例代码如下:
return "main";

客户端重定向

如果希望客户端重定向(redirect),在返回的字符串前面加上"redirect:/"。示例代码如下:
return "redirect:/main.jsp";

请求处理方法返回值是ModelAndView的情况

服务器内部跳转

若请求处理方法返回ModelAndView,则可以使用setViewName方法来说设置要转发的页面,如果直接传入字符串参数,则使用服务器内部跳转(forward)。示例代码如下:
mv.setViewName("main");

客户端重定向

如果在字符串前面加上redirect:/,则表示使用客户端重定向(redirect)。示例代码如下:
mv.setViewName("redirect:/main.jsp");

客户端重定向特点

客户端重定向无法访问WEB-INF下的资源文件

需要注意的是,由于客户端重定向(redirect)相当于在浏览器重新发请求,所以不能访问WEB-INF下的资源文件,而且也必须写资源文件后缀名,即.jsp

客户端重定向的资源文件要使用完整路径

因为此时springmvc-config.xml文件的视图解析器设置的前缀后缀都将无效,所以该资源文件要使用完整的路径。

2. 转发到控制器的请求处理方法

请求处理方法的返回值是字符串的情况

服务器内部跳转到另一个请求处理方法

若请求处理方法返回字符串,在返回的字符串前面加上"forward:/",例如return "forward:/main";,则服务器内部跳转到名为main的请求处理方法。

客户端重定向到另一个请求处理方法

若请求处理方法返回字符串,在返回的字符串前面加上"redirect:/",例如return "redirect:/main";,则客户端重定向到名为main的请求处理方法。

请求处理方法的返回值是ModelAndView对象时

若请求处理方法返回ModelAndView,调用setViewName方法设置需要转发的控制器,

服务器内部跳转到另一个请求处理方法

例如mv.setViewName("forward:/main");,则服务器内部跳转到名为main的请求处理方法。

客户端重定向到另一个请求处理方法

例如"mv.setviewName("redirect:/main");,则客户端重定向到名为main的请求处理方法。

3.2.5 ModelAndView

控制器处理方法的返回值如果是ModelAndView,则其既包含模型数据信息,也包含视图信息,这样Spring MVC将使用包含的视图对模型数据进行渲染。可以简单地将模型数据看成一个Map<String,Object>对象.

添加模型数据 addObject

在处理方法中可以使用ModelAndView对象的如下方法添加模型数据:
addObject(String attributeName,Object attributeValue);

设置视图 setViewName

可以通过如下方法设置视图:
setViewName(String viewName);

示例: ModelAndView的使用

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
package org.fkit.controller;

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

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class ModelAndViewController
{
@RequestMapping(value = "/ModelAndViewTest")
public ModelAndView ModelAndViewTest(ModelAndView mv)
{
System.out.println("ModelAndViewTest");
User user = new User();
// 设置user对象的username属性
user.setUsername("小明");
// 将User对象添加到ModelAndView当中
mv.addObject("user", user);
// 设置要转发的页面
mv.setViewName("result");
// 返回ModelAndView对象
return mv;
}
}

ModelAndViewTest方法中创建了一个自定义的User对象,并且给username属性赋值。
使用ModelAndView对象的addObject("user", user)方法将User对象添加到ModelAndView当中,即JSPrequest Scope当中。
同时调用setViewName("result")方法设置要转发的页面。

此处需要注意的是,方法的返回值必须是ModelAndView,方法的返回结果必须是ModelAndView对象,否则保存在ModelAndView对象中的"user"result.jsp页面中获取不到。

3.2.4 Model和ModelMap

在请求处理方法中可出现和返回的参数类型中,最重要的就是ModelModelAndView了。

控制器产生模型数据 视图渲染模型数据

对于MVC框架,控制器(Controller)执行业务逻辑,用于产生模型数据(Model),而视图(view)则用于渲染模型数据

传递模型数据的途径

如何将模型数据传递给视图是Spring MVC框架的一项重要工作, Spring MVC提供了多种途径输出模型数据,如:

  • ModelModelMap
  • ModelAndView
  • @ModelAttribute
  • @SessionAttributes

下面将重点介绍ModelModelMap以及ModelAndView,@SessionAttributes将在3.11节中重点介绍;@ModeLAttribute将在3.12节中重点介绍.

Model

Spring MVC在内部使用了一个org.springframework.ui.Model接口存储模型数据,它的功能类似java.util.Map接口,但是比Map易于使用。org.springframework.ui.ModelMap接口实现了Map接口。
Spring MVC在调用处理方法之前会创建一个隐含的模型对象,作为模型数据的存储容器。
如果处理方法的参数为ModelModelMap类型,则Spring MVC会将隐含模型的引用传递给这些参数。
在处理方法内部,开发者可以通过这个参数对象访问模型中的所有数据,也可以向模型中添加新的属性数据。

添加模型数据

在处理方法中, ModelModelMap对象都可以使用如下方法添加模型数据
addAttribute(String attributeName,Object attributeValue)

示例 Model和ModelMap的使用

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
package org.fkit.controller;

import org.fkit.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

// Controller注解用于指示该类是一个控制器,可以同时处理多个请求动作
@Controller
public class ModelController
{
/*
* SpringMVC在调用处理方法之前会创建一个隐含的模型对象, 作为模型数据的存储容器。
* 如果处理方法的参数为Model或ModelMap类型,则SpringMVC会将隐含模型的引用传递给这些参数。
*/
@RequestMapping(value = "/modelTest")
public String modelTest(Model model)
{
System.out.println("modelTest");
User user = new User();
// 设置user对象的username属性
user.setUsername("添加到modeMap中的数据");
// 在处理方法内部,开发者可以通过这个参数对象访问模型中的所有数据,
// 也可以向模型中添加新的属性数据。
// 将User对象添加到Model当中,也就是添加到JSP的请求域中
model.addAttribute("user", user);
// 返回视图路径
return "result1";
}

@RequestMapping(value = "/ModelMapTest")
public String modelMapTest(ModelMap modelMap)
{
System.out.println("ModelMapTest");
User user = new User();
// 设置user对象的username属性
user.setUsername("添加到modeMap中的数据");
// 将User对象添加到ModelMap当中
modelMap.addAttribute("user", user);
return "result2";
}

}

modelTest方法中创建了一个自定义的User对象,并且给username属性赋值。使用Model对象的addAttribute("user",user)方法将User对象添加到Model当中,即JSPrequest Scope当中

modelMapTest方法的代码功能与此类似,只是存储对象由Model改成了ModelMap