使用HttpMessageConverter处理JSON

HttpMessageConverter<T>工作原理:

  HttpMessageConverter<T> 是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T)并绑定到处理方法的入参中,或将对象(类型为T)输出为响应信息。对此SpringMVC提供了两种实现途径:

  1. 使用@RequestBody/@ResponseBody注解对处理方法进行标注。
  2. 使用HttpEntity<T>/ResponseEntity<T>作为处理方法的入参或返回值。

  当控制器处理方法使用到@RequestBody/@ResponseBody 或者 HttpEntity<T>/ResponseEntity<T>时,Spring首先根据请求头或响应头的Accept属性选择匹配的HttpMessageConverter,进而根据参数类型或泛型类型的过滤得到匹配的HttpMessageConverter,若找不到可用的HttpMessageConverter将会报错。
  
  下图展示了HttpMessageConverter<T>的工作原理:
image_1b1e5tcdhui21hgpn6eqo3ba01t.png-204.3kB

  DispatcherServlet默认装配了RequestMappingHandlerAdapter,我们查看Spring源码发现RequestMappingHandlerAdapter默认装配了如下的HttpMessageConverter:
  image_1b1eccehf393hvg135teo0105o3u.png-210.6kB

HttpMessageConverter<T>使用示例

  在开始介绍HttpMessageConverter<T>的用法之前,需要先导入jar包:
  
image_1b1e4vo451r1j1pc1qmm1sc5127n9.png-3.9kB

  在导入以上jar包后,RequestMappingHandlerAdapter会自动额外装配一个HttpMessageConverter对象,用于在Java对象和JSON对象之间进行转换:
  image_1b1ecs9khgn721bln28qrao04b.png-248.1kB
  
  下面的三个示例分别展示了@ResponseBody、@RequestBody和ResponseEntity<T>的使用方法,HttpEntity<T>用法类似。
  
示例一:通过@RequestBody注解将目标方法返回的Java对象转为HttpOutputMessage,并在页面中通过AJAX获取:

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
&lt;%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
&lt;title&gt;index jsp&lt;/title&gt;
&lt;script type="text/javascript"
src="${pageContext.request.contextPath }/scripts/jquery-1.9.1.min.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
$(function() {
$("#testJson").click(function() {
var url = $(this).attr("href");
var arg = {
"time" : new Date()
};
$.get(url, arg, function(data) {
for (var i = 0; i &lt; data.length; i++)
alert(data[i].id + ":" + data[i].lastName);
});
return false;
});
})
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;a href="testJson" id="testJson"&gt;Test Json&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;

controller:

1
2
3
4
5
@ResponseBody
@RequestMapping("/testJson")
public Collection&lt;Employee&gt; testJson(){
return employeeDao.getAll();
}

运行后浏览器会依次弹出“data[i].id + “:” + data[i].lastName”的提示信息:
image_1b1e5htql1qbvqkm45h1br01pi4m.png-8.8kB
image_1b1e5iuc215auou3m7164dubo13.png-9.8kB
image_1b1e5j6681i2i1jtk1t71fi010mv1g.png-10.2kB

          …
          
示例二:通过@RequestBody注解实现实现文件上传的功能:

jsp:

1
2
3
4
5
&lt;form action="testRequestBody" method="post" enctype="multipart/form-data"&gt;
File:&lt;input type="file" name="file" /&gt;
Description:&lt;input type="text" name="des" /&gt;
&lt;input type="submit" value="Submit" /&gt;
&lt;/form&gt;

controller:

1
2
3
4
5
6
7
8
@ResponseBody
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String body){
System.out.println(body);
//return "helloworld!"+new Date()保证了方法执行完后不会匹配任何一个页面
return "helloworld!"+new Date();
}

运行程序,我们上传一个test.txt文件,其中存储了某一jsp页面的信息,并在Description中输入文本值“MY TEXT”:

image_1b1eatgr614ev90sl1a1bvqlo134.png-4.4kB

提交之后,HttpMessageConverter会把上传的文件和文本值转换为一个String类型的对象,并在控制台输出:

image_1b1ea9rkce0gv63gt42mbdum2a.png-51.8kB

而且浏览器上也显示了helloworld!+时间:

image_1b1eab44h162210eaalel771c802n.png-11.8kB

注意,如果在spring的配置文件中注册了CommonsMultipartResolver类型的bean实例,如下:

1
2
3
&lt;bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"&gt;
&lt;/bean&gt;

那么@RequestBody就不能正常将上传的文件类型转换为String类型在控制台输出了,原因暂时我还不太清楚。

示例三:通过@ResponseEntity<T>实现文件下载的功能:

jsp:

1
&lt;a href="testResponseEntity"&gt;Test ResponseEntity&lt;/a&gt;

在根目录下的file目录下保存了示例二中的test.txt:

image_1b1ec0e011pq61o0v1nipd41j6f3h.png-3.2kB

controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@ResponseBody
@RequestMapping("/testResponseEntity")
public ResponseEntity&lt;byte[]&gt; testResponseEntity(HttpSession session) throws IOException{
byte[] body = null;
ServletContext context = session.getServletContext();
InputStream in = context.getResourceAsStream("/file/test.txt");
body = new byte[in.available()];
in.read(body);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename=test.txt");
HttpStatus statusCode = HttpStatus.OK;
ResponseEntity&lt;byte[]&gt; entity = new ResponseEntity&lt;byte[]&gt;(body, headers, statusCode);
return entity;
}

运行程序,点击超链接Test ResponseEntity后发现可以正确下载test.txt文件。