五、SpringMVC JSON数据交互
5.1 同步和异步回顾
5.1.1 同步请求
- 当浏览器向服务器发送同步请求时,服务处理同步请求的过程中,浏览器会处于等待的状态,只有接收到服务器的响应之后才能进行后续操作。
- 服务器处理完请求把数据响应给浏览器并覆盖浏览器内存中原有的数据,浏览器——重新加载页面(执行重定向或请求转发)并展示服务器响应的数据。
登录表单 -> 点击submit按钮
5.1.2 异步请求
- 当浏览器向服务器发送异步请求时,服务处理异步请求的过程中,浏览器无需等待服务器的响应,即可进行后续操作。
- 服务器处理完请求响应字符串数据给浏览器,浏览器——无需加载页面(不再执行重定向或请求转发),可实现页面的局部变化。
$.ajax({
url:"userNameUnique.do",
data:{
userName: $("#userName").val()
},
success:function(){
}
})
异步请求的原理:
浏览器把请求交给代理对象—XMLHttpRequest(绝大多数浏览器都内置了这个对象),由代理对象向服务器发起请求,接收、解析服务器响应的数据,并把数据更新到浏览器指定的控件上。从而实现了页面数据的局部刷新。
5.2 Ajax和JSON回顾
5.2.1 Ajax
Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
jQuery.ajax(url,[settings]):
通过 HTTP 请求加载远程数据。
jQuery 底层 AJAX 实现。简单易用的高层实现见 `$.get`, `$.post` 等。`$.ajax()` 返回其创建的 XMLHttpRequest 对象。大多数情况下你无需直接操作该函数,除非你需要操作不常用的选项,以获得更多的灵活性。
参数:
url: 一个用来包含发送请求的URL字符串。
settings: AJAX 请求设置。所有选项都是可选的。
settings常用选项:
参数名 | 参数类型 | 描述 |
---|
async | Boolean | (默认: true) 默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。 | contentType | String | (默认: “application/x-www-form-urlencoded”) 发送信息至服务器时内容编码类型。默认值适合大多数情况。如果你明确地传递了一个content-type给 $.ajax() 那么他必定会发送给服务器(即使没有数据要发送) | data | Object,String | 发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:[“bar1”, “bar2”]} 转换为 “&foo=bar1&foo=bar2”。 | dataType | String | 预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如XML MIME类型就被识别为XML。在1.4中,JSON就会生成一个JavaScript对象,而script则会执行这个脚本。随后服务器端返回的数据会根据这个值解析后,传递给回调函数 | error | Function | (默认: 自动判断 (xml 或 html)) 请求失败时调用此函数。有以下三个参数:XMLHttpRequest 对象、错误信息、(可选)捕获的异常对象。如果发生了错误,错误信息(第二个参数)除了得到null之外,还可能是"timeout", “error”, “notmodified” 和 “parsererror”。 | headers | map | Default: {},一个额外的"{键:值}"对映射到请求一起发送。此设置被设置之前beforeSend函数被调用;因此,消息头中的值设置可以在覆盖beforeSend函数范围内的任何设置。 | timeout | Number | 设置请求超时时间(毫秒)。此设置将覆盖全局设置。 | type | String | (默认: “GET”) 请求方式 (“POST” 或 “GET”), 默认为 “GET”。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。 | url | String | (默认: 当前页地址) 发送请求的地址。 |
5.2.2 JSON
- JSON(JavaScript Object Notation,JS 对象简谱) 是一种轻量级的数据交换格式;
- 基于ECMAScript的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据;
- 简洁和清晰的层次结构使得JSON成为理想的数据交换语言;
- 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率;
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
</head>
<body>
<div style="text-align: center">
<h1>首页</h1>
<a href="views/json.jsp">JSON</a><br>
<a href="views/request.jsp">请求</a><br>
<a href="views/response.jsp">响应</a><br>
</div>
</body>
</html>
json.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
<script type="text/javascript">
var obj = {
name: 'tom',
age: 20
};
var json = '{"name": "jerry", "age": 19}';
var obj2 = JSON.parse(json);
var json2 = JSON.stringify(obj);
</script>
</head>
<body>
</body>
</html>
5.3 SpringMVC处理异步请求
为实现浏览器与控制器类之间的 JSON 数据交互,Spring MVC 提供了 MappingJackson2HttpMessageConverter 实现类默认处理 JSON 格式请求响应。该实现类利用 Jackson 开源包读写 JSON 数据,将 Java 对象转换为 JSON 对象和 XML 文档,同时也可以将 JSON 对象和 XML 文档转换为 Java 对象。
5.3.1 请求
- 客户端请求的是key/value值,contentType为 application/x-www-form-urlencoded 默认值,服务器端不需要处理,即可获取数据。
- 客户端请求的是json字符串,需要指定contentType为 application/json,并且在服务器端需要使用@RequestBody注解将json串转成java对象。
5.3.2 响应
- 需要使用@ResponseBody注解将java对象转成json串输出。
5.4 实现过程
5.4.1 导入jackson包
Jackson 是最流行的 json 解析器之一。Spring MVC 的默认 json 解析器便是 Jackson。
Jackson 优点很多。Jackson 所依赖的 jar 包较少,简单易用。与其他 Java 的 json 的框架 Gson 等相比,Jackson 解析大的 json 文件速度比较快;Jackson 运行时占用内存比较低,性能比较好;Jackson 有灵活的 API,可以很容易进行扩展和定制。
Jackson 的 1.x 版本的包名是 org.codehaus.jackson ,当升级到 2.x 版本时,包名变为 com.fasterxml.jackson。
三个核心模块:
jackson-core: 核心包。定义了低级的流式API,包括了JSON处理细节。
jackson-annotations: 注解包。包含了Jackson的注解。
jackson-databind: 数据绑定包。实现了对象和JSON之间的转换,这个包依赖上面两个包。
Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象。
其他优秀的json解析工具:Google的Gson、阿里巴巴的FastJson等。
方式一:引入jar包
方式二:maven依赖配置
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
</dependencies>
注意:spring4.x须使用jackson2.x版本,而spring3.x版本使用jackson1.x版本。
5.4.2 配置
在处理器适配器中配置json转换器:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
<value>text/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
注意:如果使用<mvc:annotation-driven/> 则不用定义上边的内容。
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.newcapec.controller"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
5.4.3 POJO
Dept.java:
public class Dept {
private Integer deptno;
private String dname;
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
@Override
public String toString() {
return "Dept{" +
"deptno=" + deptno +
", dname='" + dname + '\'' +
'}';
}
}
Emp.java:
public class Emp {
private Integer empno;
private String ename;
private String job;
private Double sal;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date hiredate;
private Dept dept;
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
", hiredate=" + hiredate +
", dept=" + dept +
'}';
}
}
5.4.4 请求key/value
request.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
function sendKeyValue() {
var emp = {
empno: 1001,
ename: 'tom',
job: 'staff',
sal: 4500,
hiredate: '2022-01-09',
'dept.deptno': 10,
'dept.dname': 'HR'
};
$.ajax('sendKeyValue.do', {
type: 'post',
data: emp,
contentType: 'application/x-www-form-urlencoded',
success: function (resp) {
alert(resp);
}
});
}
</script>
</head>
<body>
<div style="text-align: center">
<p><a href="javascript:sendKeyValue()">向服务器端发送key/value值</a></p>
<p><a href="javascript:sendJson()">向服务器端发送json值</a></p>
</div>
</body>
</html>
Controller:
@Controller
public class JsonController {
@RequestMapping("sendKeyValue.do")
public void sendKeyValue(Emp emp, HttpServletResponse response) throws IOException {
System.out.println("请求数据:" + emp);
response.getWriter().write("success");
}
}
请求内容类型:
请求数据:
请求数据源:
5.4.5 请求json字符串
request.jsp页面js:
function sendJson() {
var emp = {
empno: 1002,
ename: 'jerry',
job: 'manager',
sal: 6800,
hiredate: '2022-01-09',
dept: {
deptno: 20,
dname: 'SALE'
}
};
$.ajax('sendJson.do', {
type: 'post',
data: JSON.stringify(emp),
contentType: 'application/json',
success: function (resp) {
alert(resp);
}
});
}
Controller:
@Controller
public class JsonController {
@RequestMapping("/sendJson.do")
public void sendJson(@RequestBody Emp emp, HttpServletResponse response) throws IOException {
System.out.println("请求数据:" + emp);
response.getWriter().write("success");
}
}
请求内容类型:
请求数据:
请求数据源:
5.4.6 响应单数据字符串
response.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
function responseString() {
$.ajax('responseString.do', {
type: 'get',
success: function (resp) {
alert(resp);
}
});
}
</script>
</head>
<body>
<div style="text-align: center">
<p><a href="javascript:responseString()">获取普通字符串数据</a></p>
<p><a href="javascript:responseObject()">获取对象数据</a></p>
<p><a href="javascript:responseList()">获取批量数据</a></p>
<div id="data">
</div>
<table border="1" width="500" align="center">
<thead>
<tr>
<th>员工编号</th>
<th>员工姓名</th>
<th>员工岗位</th>
<th>员工薪资</th>
<th>入职日期</th>
</tr>
</thead>
<tbody id="tab"></tbody>
</table>
</div>
</body>
</html>
Controller:
@Controller
public class JsonController {
@ResponseBody
@RequestMapping("responseString.do")
public String responseString(){
return "success";
}
}
5.4.7 响应json对象字符串
response.jsp页面js:
function responseObject() {
$.ajax('responseObject.do', {
type: 'get',
success: function(resp) {
var str = '<p>员工编号:' + resp.empno + '</p>';
str += '<p>员工姓名:' + resp.ename + '</p>';
str += '<p>员工岗位:' + resp.job + '</p>';
str += '<p>员工薪资:' + resp.sal + '</p>';
str += '<p>入职日期:' + resp.hiredate + '</p>';
$('#data').html(str);
}
});
}
Controller:
@Controller
public class JsonController {
@ResponseBody
@RequestMapping("/responseObject.do")
public Emp responseObject(){
Emp emp = new Emp();
emp.setEmpno(201);
emp.setEname("chris");
emp.setJob("manager");
emp.setHiredate(new Date());
emp.setSal(6300.0);
return emp;
}
}
测试结果:
5.4.8 响应json数组字符串
response.jsp页面js:
function responseList() {
$.ajax('responseList.do', {
type: 'get',
success: function(resp) {
var str = '';
for(var i in resp) {
var emp = resp[i];
str += '<tr>';
str += '<td>' + emp.empno + '</td>';
str += '<td>' + emp.ename + '</td>';
str += '<td>' + emp.job + '</td>';
str += '<td>' + emp.sal + '</td>';
str += '<td>' + emp.hiredate + '</td>';
str += '</tr>';
}
$('#tab').html(str);
}
});
}
Controller方法:
@Controller
public class JsonController {
@ResponseBody
@RequestMapping("/responseList.do")
public List<Emp> responseList(){
Emp emp1 = new Emp();
emp1.setEmpno(201);
emp1.setEname("chris");
emp1.setJob("manager");
emp1.setHiredate(new Date());
emp1.setSal(6300.0);
Emp emp2 = new Emp();
emp2.setEmpno(202);
emp2.setEname("tom");
emp2.setJob("manager");
emp2.setHiredate(new Date());
emp2.setSal(6300.0);
Emp emp3 = new Emp();
emp3.setEmpno(203);
emp3.setEname("cherry");
emp3.setJob("manager");
emp3.setHiredate(new Date());
emp3.setSal(6300.0);
List<Emp> list = new ArrayList<>();
list.add(emp1);
list.add(emp2);
list.add(emp3);
return list;
}
}
测试结果:
5.4.9 日期类型处理
POJO类中有日期类型的属性,只需在属性上加上@JsonFormat注解即可:
public class Emp {
private Integer empno;
private String ename;
private String job;
private Double sal;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date hiredate;
private Dept dept;
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
", hiredate=" + hiredate +
", dept=" + dept +
'}';
}
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
", hiredate=" + hiredate +
", dept=" + dept +
'}';
}
}
|