[toc]

SpringMVC处理JSON

Gson 谷歌公司的产品

fastJson 阿里的产品

JsonLib

jackson(springmvc默认使用的)

使用jackson需要引三个包:

  • jackson-annotations-2.1.5.jar
  • jackson-core-2.1.5.jar
  • jackson-databind-2.1.5.jar

1. SpringMVC使用

我们先看一下不适用json数据格式查找所有department信息,并返回department_list页面。如果熟悉这个流程可以跳过,还可以跟着再复习一下整个流程特别是xml的配置的具体含义。

1.1 depentment_list.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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<table class="table">

<thead>
<tr>
<th>Id</th>
<th>DepartmentName</th>
</tr>
</thead>

<tbody>
<c:forEach items="${list}" var="department" varStatus="index">
<tr>
<td>${department.id}</td>
<td>${department.departmentName}</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>
1.2 SpringMVC.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
<?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"
xsi:schemaLocation="
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
">
<!--上面的两个连接要放在一起-->
<context:component-scan base-package="cn.justweb"/>

<!--视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>

<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>

<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${justweb.driver}"></property>
<property name="url" value="${justweb.url}"></property>
<property name="username" value="${justweb.username}"></property>
<property name="password" value="${justweb.password}"></property>
</bean>

<bean id="template" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="datasource"></property>
</bean>


</beans>
1.3 db.properties
1
2
3
4
justweb.driver=com.mysql.jdbc.Driver
justweb.url=jdbc:mysql://cdb-o6r75r3g.bj.tencentcdb.com:10015/springmvc_day02
justweb.username=root
justweb.password=*******
1.4 web.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
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!--请求方式的过滤器-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 配置SpringMVC核心控制器:(前端控制器) -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
1.5 ☆控制层☆

传统的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//控制层
@Controller
@RequestMapping("/department")
public class DepartmentController {

//自动注入
@Autowired
DepartmentService service;

// 如果这里添加了@ResponseBody不会跳转department_list.jsp页面
@RequestMapping("/findAll")
public String findAll(Model model){
//查询所有的部门信息
List<Department> list = service.findAll();

//将查询所有的结果封装到request域中
model.addAttribute( "list",list );
/**
* 跳转,这个请求会执行凭借
* */
return "department_list";
}
}

使用@ResponseBody之后我们发现,访问得到的数据是json格式的数据,我们都知道前后端分离开发模式中,后端只需要提供数据接口(这个接口不是单指java里面的interface,而是controller,service,dao)得到json格式的数据,所以没有了跳转到jsp页面的那一步。当然使用了@ResponseBody之后也并不代表着不能进行页面跳转,我们可以使用response.sendRedirect()方法进行跳转。

1.6 service层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class EmployeeService {

@Autowired
DepartmentDao departmentDao;

public List<Employee> findAll() {
//此处查询出来的employee对象里面只存在部门的id.
List<Employee> list = dao.findAll();

//遍历list中的employee对象
for (Employee employee : list) {
//根据部门id查询出部门对象
Department department = departmentDao.findById( employee.getId() );
//重置employee对象中department对象
employee.setDepartment( department );
}
//返回集合
return list;
}
}
1.7 dao层

后面整合MyBatis,暂时使用JdbcTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Repository
public class DepartmentDao {

@Autowired
private JdbcTemplate jt;

/*查询所有*/
public List<Department> findAll(){
List<Department> list = null;
try {
list = jt.query( "select * from department", new BeanPropertyRowMapper<>( Department.class ) );
} catch (DataAccessException e) {
e.printStackTrace();
}
return list;
}
}
1.8 实体类
1
2
3
4
5
6
7
8
9
10
11
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
//部门编号
private Integer id;
//部门名称
private String departmentName;

List<Employee> list;
}
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
    //
@ResponseBody
@RequestMapping("/findAll")
public List<Department> findAll(HttpServletResponse response) throws Exception{
//查询所有的部门信息
List<Department> list = service.findAll();

System.out.println( "list = " + list );

/** 传统的方法:
//通过工具类将list转成json字符串
String s = JsonUtils.objectToJson( list );

我们学完springMVC之后就可以这样使用:@Responsebody
需要在springMVC.xml中添加mvc的命名空间和添加注解驱动
<mvc:annotation-driven/>
*/


/**
* //将查询所有的结果封装到request域中
* model.addAttribute( "list",list );
* 跳转,这个请求会执行凭借
* */
return list;

2. @RequestBody

@RequestBody 获取一个请求的请求体,也可以接受json格式的数据,并装转成java对象。

在开发中经常使用@RestController代替@Controller,我们为什么使用@RestController呢?请看@RestController的源代码你就会一目了然。

1
2
3
4
5
6
7
8
9
10
11
12
/*
*
*Spring 4.0引入了@RestController,这是一个控制器的专用版本,它是一个方便的注释,除了自动添加 *@Controller和@ResponseBody注释之外没有其他新魔法。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

}
1
2
3
4
5
6
7
8
9
@RequestMapping("/requestBody")
public String requestBody(@RequestBody String body){
//不加@RequestBody默认属性匹配
//@RequestBody可以接收Post请求的所有数据
System.out.println( "body = " + body );
//body = username=taoge&desc=lihai,使用form表单post送来的数据,不能使用get请求发送
//因为@RequestBody是在请求体中拿到数据,而get请求没有请求体
return "success";
}

3. @ResponseBody

@ResponseBody:将返回的数据放在响应体中,如果是对象,jackson自动将对象转为json格式,即SpringMVC对JSON的支持。典型spring mvc应用,请求点通常返回html页面。有时我们仅需要实际数据,如使用ajax请求。

使用@ResponseBody之后我们发现,访问得到的数据是json格式的数据,我们都知道前后端分离开发模式中,后端只需要提供数据接口(这个接口不是单指java里面的interface,而是controller,service,dao)得到json格式的数据,所以没有了跳转到jsp页面的那一步。当然使用了@ResponseBody之后也并不代表着不能进行页面跳转,我们可以使用response.sendRedirect()方法进行跳转。

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
    //查找所有department信息,并返回department_list页面。
@ResponseBody
@RequestMapping("/findAll")
public List<Department> findAll(HttpServletResponse response) throws Exception{
//查询所有的部门信息
List<Department> list = service.findAll();

System.out.println( "list = " + list );

/** 传统的方法:
//通过工具类将list转成json字符串
String s = JsonUtils.objectToJson( list );

我们学完springMVC之后就可以这样使用:@Responsebody
需要在springMVC.xml中添加mvc的命名空间和添加注解驱动
<mvc:annotation-driven/>
*/


/**
* //将查询所有的结果封装到request域中
* model.addAttribute( "list",list );
* 跳转,这个请求会执行凭借
* */
return list;
}

4. HttpEntity<T>

  • HttpEntity的方式获取请求体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RequestMapping("/httpEntity")
public String httpEntity(HttpEntity<String> httpEntity){

/**httpEntity = <,{host=[localhost:8080],
*connection=[keep-alive],
* upgrade-insecure-requests=[1],
* user-agent=[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 *(KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36],
* sec-fetch-mode=[navigate], sec-fetch-user=[?1],
* accept= [text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,;q=0.8,appli *cation/signed-exchange;v=b3], sec-fetch-site=[none],
* accept-encoding=[gzip, deflate, br],
* accept-language=[zh-CN,zh;q=0.9],
* cookie=[SL_G_WPT_TO=zh; SL_GWPT_Show_Hide_tmp=undefined; SL_wptGlobTipTmp=undefined;
* JSESSIONID=EECBD1FAA123518AD4CAA82BEDFCFE97]}
* */
System.out.println( "httpEntity = " + httpEntity );

System.out.println( "httpEntity.getBody() = " + httpEntity.getBody() );

/**httpEntity.getHeaders().getConnection() = [keep-alive]*/
System.out.println( "httpEntity.getHeaders().getConnection() = " + httpEntity.getHeaders().getConnection() );

return "success";
}

5. ResponseEntity <T>

将返回的数据放到响应体中,这个响应体使我们可控的。

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
@RequestMapping("/download")
public ResponseEntity<byte[]> download(HttpSession session)throws Exception{

//1.获取servlet对象
ServletContext servletContext = session.getServletContext();

//2.获取目标资源的真实路径
String realPath = servletContext.getRealPath( "images" );

//3.创建file对象File.separator根据操作系统自动自适应
File file = new File( realPath + File.separator + "taoge.png" );

//4. 创建输入流
FileInputStream fileInputStream = new FileInputStream( file );

//5.创建字节数组,将输入流的资源放到字节数组中
//fileInputStream.availiable 获取里面有多少流
byte[] bytes = new byte[fileInputStream.available()];

//6.将输入流放入字节数组中
fileInputStream.read(bytes);

//7.封装responseEntity对象
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Disposition","attachment;filename=taotao.png");

//设置状态码
HttpStatus status = HttpStatus.OK;

return new ResponseEntity<>( bytes, httpHeaders, status );;
}

6. HttpMessageConverter

我们通过上边几个注解的学习,你是否有这样 的疑问–是怎么把对象转成json,又怎么把json数据转成对象的?那么我们就需要了解一下–HttpMessageConverter 是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息。

clip_image0ff2

对上图的解释:

请求报文转成HttpInputMessage输入流,经过HttpMessageConverter转成java对象

java对象经HttpMessageConverter转成输出流HttpOutputMessage,根据输出流生成响应报文。

到这里你可能对HttpMessageConverter产生浓厚的兴趣–你可以根据这篇文章debug去探索一下。HttpMessageConverter是这样转换数据的