示例 自定义HttpMessageConverter 接收JSON格式的数据

Spring默认使用Jackson处理JSON数据。在实际开发中,开发者也可以选择使用其他开源框架处理JSON数据。那么,如果使用其他的开源框架处理JSON数据,该如何配置HttpMessageConverter呢?接下来,我们就使用在业界非常受欢迎的Fastjson来接收JSON数据。

下载Fastjson

本书成书时Fastjson开源框架的最新版本是1.2.9。jar包只有1个:fastjson-1.2.9.jar。建议读者进入该地址下载该版本或者更高版本进行测试。

项目示例

创建一个FastjsonTest项目,在WebContent目录下创建一个js目录,加入jQueryjson2js文件,在WEB-INF/lib目录中加入Fastjsonjar文件。

项目结构

展开/折叠
D:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\FastjsonTest
├─src\
│ └─org\
│   └─fkit\
│     ├─controller\
│     │ └─BookController.java
│     └─domain\
│       └─Book.java
└─WebContent\
  ├─index.jsp
  ├─js\
  │ ├─jquery-1.11.0.min.js
  │ ├─jquery-migrate-1.2.1.min.js
  │ └─json2.js
  ├─META-INF\
  │ └─MANIFEST.MF
  └─WEB-INF\
    ├─lib\
    │ ├─commons-logging-1.2.jar
    │ ├─fastjson-1.2.9.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

BookController.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 javax.servlet.http.HttpServletResponse;
import org.fkit.domain.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import com.alibaba.fastjson.JSONObject;

@Controller
@RequestMapping("/json")
public class BookController
{
// @RequestBody可以将json数据转换成对应的Object
@RequestMapping(value = "/testRequestBody")
public void setJson(@RequestBody Book book, HttpServletResponse response)
throws Exception
{
// JSONObject-lib包是一个beans,collections,maps,java arrays和xml和JSON互相转换的包。
// 使用JSONObject将book对象转换成json输出
System.out.println(JSONObject.toJSONString(book));
book.setAuthor("作者名字");
response.setContentType("text/html;charset=UTF-8");
// 将book对象转换成json写出到客户端
response.getWriter().println(JSONObject.toJSONString(book));
}
}

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
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
<?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:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">

<!-- spring可以自动去扫描base-pack下面的包或者子包下面的java文件,-->
<!-- 如果扫描到有Spring的相关注解的类,则把这些类注册为Spring的bean -->
<context:component-scan
base-package="org.fkit.controller" />
<!-- 静态资源处理 -->
<mvc:default-servlet-handler />

<!-- 设置配置方案 -->
<mvc:annotation-driven>
<!-- 设置不使用默认的消息转换器 -->
<mvc:message-converters register-defaults="false">
<!-- 配置Spring的转换器 -->
<bean
class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean
class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
<bean
class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean
class="org.springframework.http.converter.BufferedImageHttpMessageConverter" />
<!-- 配置fastjson中实现HttpMessageConverter接口的转换器 -->
<bean id="fastJsonHttpMessageConverter"
class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!-- 加入支持的媒体类型:返回contentType -->
<property name="supportedMediaTypes">
<list>
<!-- 这里顺序不能反,一定先写text/html,不然ie下会出现下载提示 -->
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

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

</beans>

以上配置文件和之前的配置文件主要的区别在于:

  • 之前使用的是Spring中默认的MappingJackson2HttpMessageConverter,这样只需要配置默认的<mvc:annotation-driven/>就可以了。
  • 而现在使用第三方的开源框架Fastjson处理JSON数据,则需要另行配置HttpMessageConverter

Spring MVC默认使用MappingJackson2JsonView转换器,所以必须加入Jackson这个库的第三方类文件。而在实际开发中,更加受欢迎的是Fastjson,所以本例并没有使用Jackson,而是使用了Fastjson,则转换器需要配置成com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter类型,该类是Fastjson中实现了HttpMessageConverter接口的类。
如果加入了Fastjson相关jar文件,但是没有配置FastJsonHttpMessageConverter转换器,则在发送请求时后台会提示错误:

1
Handler execution resulted in exception: Content type application/json;charset=UTF-8 not supported

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

测试

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

1
http://localhost:8080/FastjsonTest/

浏览器显示效果:

1
2
3
编号:1
书名:书的名字
作者:作者名字

由此可知,处理JSON格式的开源框架使用JacksonFastjson,只是需要使用不同的HttpMessageConverter而已.

示例 @RequestBody接收JSON格式的数据

创建一个RequestBodyTest项目,在WebContent目录下创建一个js目录,加入jQueryjson2js文件,在WEB-INF/lib目录中加入Jacksonjar文件。

项目结构

展开/折叠
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
D:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\RequestBodyTest
├─src\
│ └─org\
│ └─fkit\
│ ├─controller\
│ │ └─BookController.java
│ └─domain\
│ └─Book.java
└─WebContent\
├─index.jsp
├─js\
│ ├─jquery-1.11.0.min.js
│ ├─jquery-migrate-1.2.1.min.js
│ └─json2.js
├─META-INF\
│ └─MANIFEST.MF
└─WEB-INF\
├─lib\
│ ├─commons-logging-1.2.jar
│ ├─jackson-annotations-2.9.2.jar
│ ├─jackson-core-2.9.2.jar
│ ├─jackson-databind-2.9.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

RequestBodyTest\WebContent\index.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
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
<%@ 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>测试接收JSON格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="js/json2.js"></script>
<script type="text/javascript">
// DOM加载完毕后就直接发送请求
$(document).ready(function() {
testRequestBody();
});
function testRequestBody() {
//发送ajax请求
$.ajax(// 发送请求的URL字符串。
"${pageContext.request.contextPath}/json/testRequestBody", {
// 预期服务器返回的数据类型。
dataType : "json",
// 请求方式 POST或GET
type : "post",
// 发送信息至服务器时的内容编码类型
contentType : "application/json",
// 发送到服务器的数据。
data : JSON.stringify({
id : 1,
name : "一本书的名字"
}),
// 默认设置为true,此时所有请求均为异步请求。
// 如果设置为false,则发送同步请求
async : true,
// 请求成功后的回调函数。
success : function(data) {
console.log(data);
//使用接收到的数据更新网页内容
$("#id").html(data.id);
$("#name").html(data.name);
$("#author").html(data.author);
},
// 请求出错时调用的函数
error : function() {
alert("数据发送失败");
}
});
}
</script>
</head>
<body>
编号:
<span id="id"></span>
<br> 书名:
<span id="name"></span>
<br> 作者:
<span id="author"></span>
<br>
</body>
</html>

index.jsp页面代码分析如下:
(1)页面使用jQuery发送JSON数据,在页面的<head>部分,引入了jQueryjson2js文件。
(2)页面载入时调用testRequestBody函数。
(3)testRequestBody函数的

  • 第一个参数表示要请求的URL是:"json/testRequestBody",
  • 第二个参数是这次请求相关的一些设置选项,这些选项解释如下:
    • contentType选项:contentType:"application/json",,其表示发送的内容编码格式为JSON
    • data选项:data : JSON.stringify({id : 1,name : "一本书的名字"}),表示发送一个JSON数据;
    • success选项表示:如果请求成功则将接到的JSON数据设置到页面的<span>当中

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

import javax.servlet.http.HttpServletResponse;
import org.fkit.domain.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import com.fasterxml.jackson.databind.ObjectMapper;

@Controller
@RequestMapping("/json")
public class BookController
{
// @RequestBody根据json数据,转换成对应的Object
@RequestMapping(value = "/testRequestBody")
// @RequestBody Book book:使用@RequestBody获取JSON数据,
// 然后将JSON数据设置到对应的Book对象的属性之中.
// 第二个参数 HttpServletResponse response用来输出响应数据到客户端
public void setJson(@RequestBody Book book, HttpServletResponse response)
throws Exception
{
// ObjectMapper类是Jackson库的主要类。
// 它提供一些功能将Java对象转换成对应的JSON格式的数据
ObjectMapper mapper = new ObjectMapper();
// 将book对象转换成json,并输出到控制台
System.out.println(mapper.writeValueAsString(book));
// 设置完整的信息
book.setAuthor("小明");
// 设置响应类型
response.setContentType("text/html;charset=UTF-8");
// 将book对象转换成json写出到客户端
response.getWriter().println(mapper.writeValueAsString(book));
}
}

setJson方法中的

  • 第一个参数@RequestBody Book book表示,使用@RequestBody注解获取JSON数据后,将JSON数据设置到对应的Book对象的属性当中。
  • 第二个参数是HttpServletResponse对象,用来输出响应数据到客户端。

向前台JSP页面的JSON数据中传入了idname,为了测试接收数据,使用System.out.println(mapper.writeValueAsString(book));代码将接收到的JSON数据中的book对象打印在控制台上。
为了测试传递数据到JSP页面,在该方法中还给book对象的author对象设置了个值,并将其写出到客户端.

Book.java

RequestBodyTest\src\org\fkit\domain\Book.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.domain;

import java.io.Serializable;

public class Book implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private String author;
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getAuthor()
{
return author;
}
public void setAuthor(String author)
{
this.author = author;
}
@Override
public String toString()
{
return "Book [id=" + id + ", name=" + name + ", author=" + author + "]";
}
}

Book类中定义了3个属性:idnameauthor,用于接收向JSP页面传入的JSON数据toString方法用来输出获取的数据对象信息.

springmvc-config.xml

RequestBodyTest\WebContent\WEB-INF\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
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.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-4.3.xsd">

<!-- spring可以自动去扫描base-pack下面的包或者子包下面的java文件, -->
<!-- 如果扫描到有Spring的相关注解的类,则把这些类注册为Spring的bean -->
<context:component-scan
base-package="org.fkit.controller" />
<!-- 默认配置方案 -->
<mvc:annotation-driven />
<!-- 静态资源处理 -->
<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>

在引入静态文件,例如jQuery.js时,需要加入<mvc:default-servlet-handler />从而使用默认的Servlet来响应静态文件。如果没有加入该配置,则执行时页面会报404错误,而控制台会提出警告:

测试

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

1
http://localhost:8080/RequestBodyTest/

载入index.jsp页面时会发送Ajax请求,并传递JSON数据, BookController接收到请求后,@RequestBody注解会将接收到的JSON数据设置到Book形式参数对应的属性当中。控制台输出如下:

1
{"id":1,"name":"一本书的名字","author":null}

可以看到,JSON数据传递的idname被赋值到Book对象的属性中。接下来, setJson方法给Book对象的author属性设置了值,并将Book对象转换成JSON写出到客户端。
此时客户端显示效果如下:

1
2
3
 编号: 1
书名: 一本书的名字
作者: 小明

这表示Spring MVC成功将Book对象被以JSON数据格式成功写到客户端了。

个人总结

使用JSON交换的步骤:

  1. 使用jQuery发送ajax请求,并将数据以JSON格式发送.
  2. 引入Jacksonjar包,使用@RequstBody接收数据,该注解可以解析请求体中的JSON数据,并设置到其后面的对象上。
  3. 使用Jackson包中的ObjectMapper对象.writeValueAsString(Java对象)方法,将Java对象转为JSON字符串.
  4. 使用HttpServletResponse写到客户端即可.
  5. 使用jQuery根据接收到的JSON数据操作DOM即可。

3.13.2 转换JSON数据

Spring MVC提供了处理JSON格式请求/响应HttpMessageConverter:

  • MappingJackson2HttpMessageConverter

使用Jackson

利用Jackson开源类包处理JSON格式的请求或响应信息

使用JSON通信需要的配置

因此,只需要:

  • Spring Web容器中为RequestMappingHandlerAdapter装配处理JSONHttpMessageConverter,
  • 并在交互过程中通过请求的Accept指定MIME类型,

服务端的处理方法和客户端就可使用JSON格式的消息进行通信了,开发者几乎不用关心通信层数据格式的问题,可以将精力集中到业务处理上面。

@RequestBody注解的作用:读取body数据 解析 绑定到参数上

org.springframework.web.bind.annotation.RequestBody注解用于读取Request请求的body部分数据,然后使用系统默认配置的HttpMessageConverter进行解析,接着把相应的数据绑定到Controller中方法的参数上。

使用请求头的ContentType指定数据编码格式

当前台页面使用GETPOST方式提交数据时,数据编码格式由请求头的ContentType指定。可以分为以下几种情况:

数据编码格式 可以处理的注解
application/x-www-form-urlencoded 这种情况的数据@RequestParam@ModelAttribute也可以处理,并且很方便,当然@RequestBody也能处理。
application/jsonapplication/xml等格式的数据。 必须使用@RequestBody来处理。
multipart/form-data @RequestBody不能处理这种格式的数据。

在实际开发工作中使用@RequestBody注解可以很方便地接收JSON格式的数据,并将其转换成对应的数据类型
根据Spring的官方文档说明,Spring MVC默认使用MappingJackson2HttpMessageConverter转换JSON格式的数据,Spring默认使用Jackson开源框架作为JSON解析框架
Jackson开源框架可以非常轻松地将Java对象转换成JSON对象和xml文档,同样也可以将JSON对象、xml文档转换成Java对象.

Jackson下载

本书成书时Jackson开源框架的最新版本是2.9.2,Jacksonjar包共有3个`:

  • jackson-annotations-2.9.2.jar
  • jackson-core-2.9.2.jar,
  • jackson-databind-2.9.2.jar

下载地址

3.13 @RequestBody注解

用途:处理JSON、XML等数据

org.springframework.web.bind.annotation.RequestBody注解常用来处理ContentType不是application/x-ww-form-urlencoded编码的内容,例如application/json,application/xml等。

使用HttpMessageConverters类解析JSON、XML

@RequestBody注解通过使用HandlerAdapter配置的HttpMessageConverters来解析JSONxml数据,然后绑定到相应的Bean上。

3.13.1 HttpMessageConverter接口

用途

HttpMessageConverter<T>Spring3.0之后新增的一个重要接口,它负责将请求信息转换为一个对象(类型为T),并将对象(类型为T)绑定到请求方法的参数上或输出为响应信息

由RequestMappingHandlerAdapter使用

DispatcherServlet默认已经装配了RequestMappingHandlerAdapter作为HandlerAdapter组件的实现类,即:HttpMessageConverterRequestMappingHandlerAdapter使用,将请求信息转换为对象,或将对象转换为响应信息

HttpMessageConverter接口方法

HttpMessageConverter<T>接口中定义了以下几个方法

方法 描述
boolean canRead(Class<?>clazz,MediaType mediaType) 该方法指定转换器可以读取的对象类型,
即转换器可将请求信息转换为clazz类型的对象,
同时指定支持的MIME类型(text/htmlapplication/json等)。
MIME媒体类型在RFC2616中定义,
具体请参考http://tools.ietf.org/html/rfc2616#section-3.7上的说明。
boolean canWrite(Class<?>clazz,MediaType mediaType) 该方法指定转换器可以将clazz类型的对象写到响应流当中,
响应流支持的媒体类型在mediaType中定义。
List<MediaType> getSupportedMediaTypes() 该方法返回当前转换器支持的媒体类型。
T read(Class<?extendsT> clazz,HttpInputMessage inputMessage) 该方法将请求信息流转换为T类型的对象
void write(T t,MediaType contentType,HttpOutputMessage outputMessage) 该方法将T类型的对象写到响应流当中,同时指定响应的媒体类型为contentType

Spring提供的HttpMessageConverter接口实现类

SpringHttpMessageConverter<T>提供了多个实现类,这些实现类组成了一个功能强大、用途广泛的信息转换家族。详细说明如下:

实现类 描述
StringHttpMessageConverter
  • 将请求信息转换为字符串,
  • 泛型T为String类型,
  • 可以读取所有媒体类型(*/*)的请求信息,可通过设置supportedMediaTypes属性指定媒体类型。响应信息的媒体类型为text/plain(即Content-Type的值)。
FormHttpMessageConverter
  • 将表单数据读取到MultiValueMap中,
  • 泛型T为org.springframework.util.MultiValueMap<String,?>类型,
  • 支持读取application/x-www-form-urlencoded类型的信息,但不支持读取multipart/form-data类型的信息。
  • 可以写application/x-www-form-urlencodedmultipart/form-data类型的响应信息。
SourceHttpMessageConverter 如果部分表单属性是XML数据,则可用该转换器进行转换。
ResourceHttpMessageConverter
  • 读写org.springframework.core.io.Resource对象,
  • 泛型T为org.springframework.core.io.Resource对象,
  • 可以读取所有媒体类型(*/*)的请求信息。如果类路径下提供了JAFJavaActivationFramework),则根据Resource的类型指定响应的类型,否则响应的类型为application/octet-stream
BufferedImageHttpMessageConverter
  • 读写BufferedImage对象,
  • 泛型T为BufferedImage对象,
  • 可以读取所有类型(*/*)的请求信息,返回BufferedImage相应的类型,也可以通过contentType显式指定。
ByteArrayHttpMessageConverter
  • 读写二进制数据,
  • 泛型T为byte[]类型,
  • 可以读取所有类型(*/*)的请求信息,可以通过设置supportMediaTypes属性指定类型,响应信息的媒体类型为application/octet-stream
SourceHttpMessageConverter
  • 读写javax.xml.transform.Source类型的数据,
  • 泛型T为javax.xml.transform.Source类型及其扩展类型包括:
    • javax.xml.transform.dom.DOMSource
    • javax.xml.transform.sax.SAXSource
    • javax.xml.transform.stream.StreamSource;
  • 可以读取text/xmlapplication/xml类型请求,响应信息的类型为text/xmlapplication/xml
MarshallingHttpMessageConverter
  • 通过Springorg.springframework.oxm.Marshalling(将Java对象转换成XML)和Unmarshaller(将XML解析为Java对象)读写XML消息
  • 泛型T为Object类型;可以读取text/xmlapplication/xml类型请求,
  • 响应信息的类型为text/xmlapplication/xml
Jaxb2RootElementHttpMessageConverter
  • 通过JAXB2读写XML消息,将请求消息转换到注解@XmlRootElementXmlType作用的类中。
  • 泛型T为Object类型;
  • 可以读取text/xmlapplication/xml类型请求,响应信息的类型为text/xmlapplication/xml
MappingJackson2HttpMessageConverter
  • 利用Jackson开源类包读写JSON数据。
  • 泛型T为Object类型;
  • 可以读取application/json类型的数据,响应信息的类型为application/json
RssChannelHttpMessageConverter
  • 能够读写RSS种子消息;
  • 泛型T为com.sun.syndication.feed.rss.Channel类型;
  • 可以读取application/rss+xml类型的数据,响应信息的类型为application/rss+xml
AtomFeedHttpMessageConverter
  • 能够读写RSS种子消息。
  • 泛型T为com.sun.syndication.feed.atom.Feed类型;
  • 可以读取application/atom+xml类型的数据,响应信息的类型为application/atom+xml

默认装配的HandlerAdapter实现类

DispatcherServlet默认已经装配了RequestMappingHandlerAdapter作为HandlerAdapter组件的实现类;RequestMappingHandlerAdapter默认已经装配了以下的HttpMessageConverter:

  • StringHttpMessageConverter
  • ByteArrayHttpMessageConverter
  • SourceHttpMessageConverter

自定义RequestMappingHandlerAdapter

如果需要装配其他类型的HttpMessageConverter,则可以在SpringWeb容器的上下文中自定义一个RequestMappingHandlerAdapter,如下所示:

1
2
3
4
5
6
7
8
9
10
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.comverter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.comverter.xml.SourceHttpMessageConverter"/>
<bean class="org.springframework.http.comverter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.comverter.BufferedImageHttpMessageConverter"/>
</list>
</property>
</bean>

自定义后默认装配的HttpMessageConverter

提示如果在Spring Web容器中显式定义了一个RequestMappingHandlerAdapter,则Spring MVCRequestMappingHandlerAdapter默认装配的HttpMessageConverter将不再起作用。

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注解的使用方法有很多种,非常灵活,读者可以根据业务需求选择使用。

3.11 @SessionAttributes注解

用途:将Model中的指定属性 转存到 HttpSession对象中

org.springframework.web.bind.annotation.SessionAttributes注解允许我们有选择地指定Model中的哪些属性转存到HttpSession对象当中

属性

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

属性 类型 是否必要 说明
names String数组 Model中要存储到HttpSession当中的属性名称
value String数组 names属性的别名
types Class<?>数组 指示参数是否必须绑定

使用范围:类

@SessionAttributes注释只能声明在类上,而不能声明在方法上。

示例 @SessionAttributes注解的使用

项目结构

展开/折叠
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\SessionAttributesTest
├─src\
│ └─org\
│ └─fkit\
│ ├─controller\
│ │ └─SessionAttributesController.java
│ └─domain\
│ └─User.java
└─WebContent\
├─index.jsp
├─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

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

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

@Controller
// 将Model中名为user的的属性转存HttpSession对象当中
@SessionAttributes("user")
public class SessionAttributesController
{

// 该方法映射的请求为http://localhost:8080/SessionAttributesTest/login
// 把表单提交的请求参数loginname赋值给方法的loginname参数
@RequestMapping(value = "/login")
public String login(@RequestParam("loginname") String loginname,
@RequestParam("password") String password, Model model)
{
// 创建User对象,装载用户信息
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
user.setUsername("admin");
// 将user对象添加到Model当中
model.addAttribute("user", user);
// 浏览器端重定向到其他请求处理方法,这样会重新生成一个请求对象
// 因为是新的对象,所以request作用域内将不再存在user属性
return "redirect:/sessionScope";
}
@RequestMapping(value = "/sessionScope")
public String sessionScope()
{
return "welcome";
}
}

index.jsp

接下来创建一个登录页面index.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>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试@SessionAttributes注解</title>
</head>
<body>
<h3>测试@SessionAttributes注解</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>

welcome.jsp

最后创建一个查看作用域中属性数据的页面welcome.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
32
<%@ 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>测试@SessionAttributes注解</title>
</head>
<body>
<table border="1px">
<tr>
<th>作用域</th>
<th>登陆名</th>
<th>用户名</th>
<th>密码</th>
</tr>
<tr>
<td>访问request作用范围域中的user对象:</td>
<!-- 不存在,将什么也不显示 -->
<td>${requestScope.user.loginname }</td>
<td>${requestScope.user.username }</td>
<td>${requestScope.user.password }</td>
</tr>
<tr>
<td>访问session作用范围域中的user对象:</td>
<td>${sessionScope.user.loginname }</td>
<td>${sessionScope.user.username }</td>
<td>${sessionScope.user.password }</td>
</tr>
</table>
</body>
</html>

测试

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

1
http://localhost:8080/SessionAttributesTest/

输入登录名"小明",密码"XiaoMing",单击”登录”按钮。请求将会被提交到SessionAttributesController类的login方法,该方法将会创建User对象来保存数据并将其设置到Model当中。
因为类上面使用了@SessionAttributes注解,所以**User同时也会被设置到HttpSession作用域当中**。方法执行完后会跳转到欢迎界面,欢迎页面内容如下:

1
2
3
作用域     登陆名     用户名     密码
访问request作用范围域中的user对象: 小明 admin XiaoMing
访问session作用范围域中的user对象: 小明 admin XiaoMing

可以看到,User对象被成功设置到HttpSession作用域当中。

@SessionAttributes其他写法

@SessionAttributes还有如下写法:

1
@SessionAttributes(types={User.class}, value="user")

还可以设置多个对象到HttpSession当中:

1
@SessionAttributes(types={User.class, Dept.class}, value=("user","dept"))

其中

  • types属性用来指定放入HttpSession当中的对象类型
  • value属性或者name属性用来指定Model中要存储到HttpSession当中的属性名称

示例 @RequestAttribute注解和@SessionAttribute注解的使用

新建一个项目AttributeTest,加入所需的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\AttributeTest
├─src\
│ └─org\
│ └─fkit\
│ ├─controller\
│ │ └─AttributeController.java
│ └─filter\
│ └─TestAttributeFilter.java
└─WebContent\
├─index.jsp
├─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

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>@RequestAttribute@SessionAttribute测试</title>
</head>
<body>
<h2>@RequestAttribute@SessionAttribute测试</h2>
<a href="attrbuteTest">测试@RequestAttribute@SessionAttribute</a>
<br>
</body>
</html>

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class AttributeController
{
// 测试@CookieValue注解
// 该方法映射的请求为 /cookieValueTest
@GetMapping(value = "/attrbuteTest")
public ModelAndView attrbuteTest(ModelAndView mv)
{
System.out.println("attributeTest方法被调用...");
// 客户端重定向到main请求,会被自定义过滤器拦截
mv.setViewName("redirect:main");
return mv;
}
/*
* 该方法映射的请求为/main
* @RequestAttribute("name") String name---将request作用范围中"name"属性的值赋给方法的参数name
* @SessionAttribute("sex") String sex---将session作用范围中"sex"属性的值赋给方法的参数sex
*/
@RequestMapping("/main")
public String main(@RequestAttribute("name") String name,
@SessionAttribute("sex") String sex)
{
System.out.println("main方法被调用...");
// 输出@RequestAttribute获得的name
System.out.println("name: " + name);
// 输出@SessionAttribute获得的sex
System.out.println("sex: " + sex);
return "welcome";
}
}
  • attributeTest方法处理请求后重定向到main请求,
  • main请求会被自定义过滤器拦截,
  • 在过滤器中会分别设置两个属性到request作用域和session作用域。
  • main方法中使用@RequestAttribute@SessionAttribute进行赋值。

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

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

// 过滤器拦截/main请求
@WebFilter(value = "/main")
public class TestAttributeFilter implements Filter
{
@Override
public void destroy()
{
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
System.out.println("进入AuthFilter过滤器的doFilter方法");
// 将ServletRequest对象强转成HttpServletRequest对象
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 在request作用范围域中设置一个name属性
httpRequest.setAttribute("name", "小明");
// 在session作用范围域中设置一个sex属性
httpRequest.getSession().setAttribute("sex", "男");
// 如果还有过滤器执行过滤器,否则进入请求处理方法
chain.doFilter(httpRequest, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException
{
}
}

TestAttributeFilter过滤器拦截"main"请求,在dofilter方法中分别设置两个属性到request作用域和session作用域。

测试

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

1
http://localhost:8080/AttributeTest/

单击测试@RequestAttribute和@SessionAttribute"超链接发送请求,将调用attrbuteTest方法,然后经过过滤器,重定向到main方法,控制台输出结果如下:

1
2
3
4
5
attributeTest方法被调用...
进入AuthFilter过滤器的doFilter方法
main方法被调用...
name: 小明
sex: 男

浏览器显示内容:

1
2
name:小明
sex:男

可以看到,request作用域中的name的值被传递到请求处理方法mainname变量, session作用域中的sex的值被传递到请求处理方法mainsex变量,并输出打印在控制台。

3.10 @SessionAttribute注解

用途:session作用域的属性 赋值给 形参

org.springframework.web.bind.annotation.SessionAttribute注解用session作用域中的属性赋值给目标方法的形式参数,这些属性由请求处理方法、过滤器或拦截器创建并存在于session作用域中。

属性

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

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

示例代码

@SessionAttribute注解示例代码如下:

1
2
3
@RequestMapping(value="/arrtibuteTest")
public void arrtibuteTest(
@SessionAttribute(value="username") String username) {...}

以上代码会自动session作用域中名为username的属性的值设置到请求处理方法的username形式参数上

3.9 @RequestAttribute注解

处理 属性类型 的注解:
@RequestAttribute@SessionAttribute@SessionAttributes@ModelAttribute

用途:request作用域中的属性 赋值给 请求处理方法的形参

org.springframework.web.bind.annotation.RequestAttribute注解用于访问由请求处理方法、过滤器或拦截器创建的、预先存在于request作用域中的属性,并将该request作用域中的属性的值设置到请求处理方法的形式参数上
(将request作用域中的属性值赋值给请求处理方法的形参)

属性

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

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

示例代码

@RequestAttribute注解示例代码如下:

1
2
3
@RequestMapping(value="/arrtibuteTest")
public void arrtibuteTest(
@RequestAttribute(value="username") String username){ ... }

以上代码会自动request作用域中名为username的属性的值设置到请求处理方法的username参数上

3.8 @CookieValue注解

处理 请求头 部分的注解:@RequestHeader,@CookieValue

用途

org.springframework.web.bind.annotation.CookieValue注解用于将请求的Cookie数据映射到请求处理方法的形式参数上

属性

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

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

代码示例

@CookieValue注解示例代码如下:

1
2
3
4
5
6
@RequestMapping(value="/cookieValueTest")
public void cookieValueTest(
@CookieValue(value="JSESSIONID", defaultValue="") String sessionId)
{
...
}

以上配置会自动将CookieJSESSIONID的值设置到方法的sessionId形式参数上, defaultValue表示Cookie中没有JSESSIONID时默认设置sessionId为空字符串。

示例:@CookieValue注解的使用

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

项目结构

展开/折叠
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\03\CookieValueTest
├─src\
│ └─org\
│   └─fkit\
│     └─controller\
│       └─CookieValueController.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

示例代码如下:

CookieValueController.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.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class CookieValueController
{
// 测试@CookieValue注解
// 该方法映射的请求为 /cookieValueTest
@GetMapping(value = "/cookieValueTest")
// 将cookie中的JSESSIONID值赋值给方法的参数sessionId
public void cookieValueTest(
@CookieValue(value = "JSESSIONID", defaultValue = "") String sessionId)
{
System.out.println("通过@CookieValue获得JSESSIONID: " + sessionId);
}
}

cookieValueTest方法会自动将CookieJSESSIONID的值赋值给sessionId形式参数,如果Cookie中没有JSESSIONID时默认设置sessionId为空字符串。

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>@CookieValue测试</title>
</head>
<body>
<h2>@CookieValue测试</h2>
<a href="cookieValueTest">测试@CookieValue注解</a>
<br>
</body>
</html>

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

1
http://localhost:8080/CookieValueTest/

单击”测试@CookieValue注解”超链接发送请求,将调用cookieValueTest方法,控制台输出结果如下

1
通过@CookieValue获得JSESSIONID: 22334F73E67C6C486559D98FF00539CB

可以看到,请求中JSESSIONID的值被传递到方法的sessionId变量并输出打印在控制台。