前言:在如今前后端分离的趋势下,后端基本不需要再去关心前端页面的事情,只需要把数据处理好并通过相应的接口返回数据给前端即可。在SpringMVC中,我们可以通过@ResponseBody注解来返回JSON数据或者是XML数据。一般返回的是更轻量级的JSON 格式数据,移动端的Android、IOS客户端和Web网页前端都可以利用JSON解析器很好地解析出数据并展示。相同的后台接口适用于不同的前端使用,实现了真正的前后端分离,这简直不能太爽。
一、JSON格式简介(使用过的朋友可以直接跳过这一步)
JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation),是轻量级的文本数据交换格式。类似于XML,但JSON 比 XML 更小、更快、更易解析,而且JSON 具有自我描述性,更易理解。
JSON 最常见的用法之一,是从 web 服务器上读取 JSON 数据(作为文件或作为 HttpRequest),将 JSON 数据转换为 JavaScript 对象,然后在网页中使用该数据。
1、JSON实例
Json是一种轻量级的数据交换格式,采用一种“键:值”对的文本格式来存储和表示数据,在服务器端和前端交换数据过程中常常被使用,简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
{
"employees": [
{ "firstName":"Bill" , "lastName":"Gates" },
{ "firstName":"George" , "lastName":"Bush" },
{ "firstName":"Thomas" , "lastName":"Carter" }
]
}
这个 employee 对象是包含 3 个员工记录(对象)的数组。
如何把 JSON 文本转换为 JavaScript 对象?
由于 JSON 语法是 JavaScript 语法的子集, 使用 JavaScript对象描述语法,所以无需额外的软件就能处理 JavaScript 中的 JSON。JavaScript 函数 eval() 可用于将 JSON 文本转换为 JavaScript 对象。eval() 函数使用的是 JavaScript 编译器,可解析 JSON 文本,然后生成 JavaScript 对象。
注意:eval() 函数可编译并执行任何 JavaScript 代码。这隐藏了一个潜在的安全问题。
使用 JSON 解析器将 JSON 转换为 JavaScript 对象是更安全的做法。JSON 解析器只能识别 JSON 文本,而不会编译脚本。
在浏览器中,这提供了原生的 JSON 支持,而且 JSON 解析器的速度更快。
2、JSON 语法规则
JSON 语法是 JavaScript 对象表示法语法的子集。
-
数据在名称/值对中
-
数据由逗号分隔
-
花括号保存对象
-
方括号保存数组
a、JSON 对象
JSON 对象在花括号中书写:
对象可以包含多个名称/值对:‘
{ "firstName":"John" , "lastName":"Doe" }
b、JSON 数组
JSON 数组在方括号中书写:
数组可包含多个对象:
{
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
}
在上面的例子中,对象 "employees" 是包含三个对象的数组。每个对象代表一条关于某人(有姓和名)的记录。
3、为什么使用 JSON代替 XML?
没有结束标签
更短
读写的速度更快
能够使用内建的 JavaScript eval() 方法进行解析
使用数组
不使用保留字
对于 AJAX 应用程序来说,JSON 比 XML 更快更易使用。
4、JSON数据前端解析并展示总结
JSON格式是采用JavaScript对象的描述语法,所以Web网页前端能很好把它转换为JavaScrit对象,然后在网页中使用该数据。在主流浏览器中提供了原生的 JSON 支持,而且 JSON 解析器的速度更快。
还有移动端Android和IOS也可以利用FastJson或Gson等JSON解析工具很方便高效的解析JSON数据出来并展示,甚至可以自己写代码解析也不难。
二、SpringMVC如何返回JSON格式
前面我写的Maven整合SSM框架中走视图解析器返回的是JSP网页,只适用于Web前端不够科学。我们应该返回轻量级的JSON 格式数据,然后移动端的Android、IOS app和Web网页前端都可以利用JSON解析器很好地解析出数据并展示。才能达到相同的后台接口适用于不同的前端使用的目的,实现真正的前后端分离。
我们在Web项目开发过程中,一般来说访问一个处理器,然后会返回一个视图,或者跳转到另外的处理器。但是随着项目越来越复杂,需求越来越复杂,对于处理器返回数据的类型要求也越来越多。比如要求能够返回JSON类型的数据、或者能够返回XML格式的数据,或者返回二进制的数据流等等。SpringMVC提供了这样的一个数据类型转换机制,允许控制器返回的数据不经过正常的视图处理流程,而是直接将返回的数据写入响应体中(response body)。这种机制可以根据预先设定的media type,调用合适的MessageConverter数据转换器将数据转换成合适的类型。
a、使用@ResponseBody 注解设置处理器返回值直接写入响应体
SpringMVC框架提供了一种十分简单的方式来配置控制器中的某个处理器的返回值直接写入响应体,只需要在处理器方法上添加 @ResponseBody 注解即可
@Controller
public class HomeController {
@RequestMapping("/json")
@ResponseBody
public Book jsonFun() {
Book book = new Book("spring-framework", "12345", "2016-10-12");
return book;
}
}
b、默认包含的MessageConverter数据转换器
SpringMVC默认包含一系列的数据转换器,此处不一一列举,就介绍几种常用的:
-
MappingJackson2XmlHttpMessageConverter 基于Jackson的XML转换器,能够将对象转换成XML格式的数据
-
MappingJackson2HttpMessageConverter 基于 Jackson 的JSON转换器,能够将对象转换成JSON格式的数据
-
GsonHttpMessageConverter 基于Gson的JSON转换器,能够将对象转换成JSON格式数据
对于系统中默认包含的转换器,只要我们在项目中加入转换器所依赖的JAR包,相关转换器就会被自动加载。
c、设置数据转换类型 media type
现在我们明确了如何允许控制器直接将返回结果写入响应体中,并且明确了处理器将返回的数据写入响应体之前会进行相应的转换。但是根据我们上面一节的了解,我们发现SpringMVC中默认携带的转换器不止一种,那么我们如何确定处理器返回的数据交给合适的转换器,转换成我们想要的数据类型呢?
只需要使用 @ReuqestMapping注解的一个参数进行设置即可
@RequestMapping(value="/json", produces={"application/json; charset=UTF-8"})
@ResponseBody
public Book abc() {
Book book = new Book("spring-framework", "12345", "2016-10-12");
return book;
}
d、系统默认的设置的 media type
在一些简单的环境中,比如项目返回的数据类型只需要JSON格式,可能就不需要设置 produces的类型,来指定 media type,也能够按照要求返回正常的JSON数据,这是为什么?
因为SpringMVC在项目初始化时,会去扫描系统中的JAR包,然后根据扫描到的JAR包设置默认的转换类型,大概的扫描过程是:
1)检查系统中是否存在jackson-xml的JAR包,如果存在,就将数据转换类型列表中设置XML类型,以及其对应的转换器
2)检查系统中是否存在jackson-json的JAR包,如果存在,就在数据转换类型列表中设置JSON类型,以及其对应的转换器
因为是先检测的XML,因此XML排在JSON前面,如果系统两者的JAR包都存在,那么默认情况下数据会被转换成XML格式
三、SpringMVC返回JSON格式步骤:
在SpringMVC中,我们可以通过@ResponseBody注解来返回JSON数据或者是XML数据。这个注解的作用是将控制器方法返回的对象通过适当的消息转换器转换为指定的格式之后,写入到response对象的body区,也就是HTTP响应的内容体,一般我们都是用来返回JSON数据,因为默认是按JSON格式进行转换的。需要注意的是,在使用此注解之后不会再走视图解析器,而是直接将数据写入到输出流中,他的效果等同于使用response对象输出指定格式的数据。
a、因为这个注解依赖jackson,所以要想使用这个注解,我们首先需要配置jackson的包,Maven配置依赖如下:
<!-- 添加jackson,用于SpringMVC默认就支持的json转换器MappingJackson2HttpMessageConverter(基于Jackson的JSON转换器,能够将对象转换成JSON格式的数据)-->
<!--对于系统中默认包含的json转换器,只要我们在项目中加入转换器所依赖的JAR包,相关转换器就会被自动加载。-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.7</version>
</dependency>
b、SpringMVC配置如下:
<!-- 启用包自动扫描功能,找到使用注解@Controller相关的bean注入IOC容器管理-->
<context:component-scan base-package="com.hs.controller"/>
<!-- 开启SpringMVC注解模式:提供Controller请求转发,json自动转换等功能-->
<mvc:annotation-driven/>
c、然后使用在代码中使用@ResponseBody:就能将目标方法的返回值自动转换成json格式,然后返回给前端
@ResponseBody注解可以写在类或方法上,写在类上是对该类中的所有方法都生效,写在方法上则是只对该方法生效:
package com.hs.controller;
import com.hs.dao.entity.UserEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
@Controller
public class UserController {
@Resource
private UserService userService;
/**
* 返回JSON格式测试方法
* @return
*/
@RequestMapping(value="/json",produces={"application/json; charset=UTF-8"})
@ResponseBody
public UserEntity test() {
UserEntity userEntity = new UserEntity();
userEntity.setId(6);
userEntity.setUsername("hello");
userEntity.setPassword("124");
return userEntity;
}
}
运行结果:
注意:如果需要 @ResponseBody 注解作用在类上时,我们可以直接使用 @RestController 注解,这个注解相当于@ResponseBody + @Controller注解,这样我们就不需要写两个注解了。
以上只是用了一个普通的pojo对象作为演示的返回数据,除此之外@ResponseBody 注解,可以将如下类型的数据转换成JSON格式:
-
基本数据类型,如 boolean , String , int 等
-
Map 类型数据
-
集合或数组
-
实体对象
-
实体对象集合
既然能发送数据到客户端,那么与之相对的就能接收客户端发送的数据,而@RequestBody注解可以接收客户端发送的JSON数据,并绑定到相应的方法参数上,如下示例:
import org.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class Test {
@RequestMapping("/json")
public void test(@RequestBody Student student) {
System.out.println(new JSONObject(student));
}
}
四、SpringMVC利用FastJson代替默认的消息转换器返回json数据
4.1、什么是 Fastjson?
阿里官方给的定义是, fastjson 是阿里巴巴的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean
4.2、Fastjson 的优点
速度快
fastjson相对其他JSON库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能从未被其他Java实现的JSON库超越。使用广泛
fastjson在阿里巴巴大规模使用,在数万台服务器上部署,fastjson在业界被广泛接受。在2012年被开源中国评选为最受欢迎的国产开源软件之一。测试完备
fastjson有非常多的testcase,在1.2.11版本中,testcase超过3321个。每次发布都会进行回归测试,保证质量稳定。使用简单
fastjson的 API 十分简洁。功能完备
支持泛型,支持流处理超大文本,支持枚举,支持序列化和反序列化扩展。
4.3、为什么要使用Fastjson?
1、fastjson是目前java语言中最快的json库,比自称最快的jackson速度还要快
2、使用fastjson的序列化和反序列化替换java serialize,java serialize不但性能慢,而且体制大。
3、使用fastjson替换hessian,json协议和hessian协议大小差不多一样,而且fastjson性能优越,10倍于hessian
4、把fastjson用于memached缓存对象数据。
Jackson在处理对象之前的循环嵌套关系时不便。
什么是对象间的循环嵌套?比如A有一个List,B对象里又有一个A对象,当然返回A对象的Json字符串时,如果是
Jackson就会发生异常,因为Jackson天生不具备处理这种关系的能力,而Fastjson正好具备了这种能力
(另,如果你用的是 Jackson,可以使用相应的注解来支持对象间的循环嵌套,具体是什么注解忘了,你可以Google一下Jackson循环嵌套就有很多答案)。
4.4、SpringMVC利用速度更快的FastJson返回json数据
a、添加Maven依赖,以前导入的jackson相关依赖可以移除掉
<!-- 添加fastjson,效率更好并且适应场景更多,可以用来代替SpringMVC默认的jackson或Gson数据转换器-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
b、配置SpringMVC.xml
Spring4.3.5版本以上得这样正确配置:
<!--配置fastjson为默认JSON解析器:使用fastjson代替jackjson返回json格式 -->
<!--修改mappingJacksonHttpMessageConverter的 class值为FastJson的MessageConverter就可以了-->
<bean id="mappingJacksonHttpMessageConverter"
class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!--class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">-->
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
<value>application/xml;charset=UTF-8</value>
</list>
</property>
</bean>
注意Spring版本比较新的不要用网上很多人复制所说的方式,如下:
我的版本会报错显示<mvc:message-converters> is not allowed here错误。
c、方法返回值改为String,必须添加JSONObject.toJSONString(userEntity)这句,手动转换实体类为json格式数据再返回
package com.hs.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hs.dao.entity.UserEntity;
import com.hs.service.UserService;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.DelegatingServletInputStream;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@Controller
public class UserController {
private static Logger logger = Logger.getLogger(UserController.class);
@Autowired
private UserService userService;
/**
* 1、方法返回值改为String,不再是以前的代转换实体类
* @return
*/
@RequestMapping(value="/fastjson1",produces={"application/json; charset=UTF-8"})
@ResponseBody
public String test() {
UserEntity userEntity = new UserEntity();
userEntity.setId(6);
userEntity.setUsername("hello");
userEntity.setPassword("124");
//必须添加这句手动转换实体类为json格式数据再返回
String str = JSONObject.toJSONString(userEntity);
return str;
}
/**
* 2、当属性值为空的时候,fastjson默认是不输出的,
* @return
*/
@RequestMapping(value="/fastjson2",produces={"application/json; charset=UTF-8"})
@ResponseBody
public String fastjsonTest() {
Map< String , Object > jsonMap = new HashMap< String , Object>();
jsonMap.put("a",1);
jsonMap.put("b","");
jsonMap.put("c",null);//当属性值为空的时候,fastjson默认是不输出的
jsonMap.put("d","hs");
String str = JSONObject.toJSONString(jsonMap);
System.out.println(str);
return str;
}
}
test()方法返回结果:
fastjsonTest()返回结果:
从输出结果可以看出,null对应的key已经被过滤掉;这明显不是我们想要的结果,这时我们就需要用到fastjson的SerializerFeature序列化属性。也就是这个方法:JSONObject.toJSONString(Object object, SerializerFeature... features)
Fastjson的SerializerFeature序列化属性:
QuoteFieldNames———-输出key时是否使用双引号,默认为true
WriteMapNullValue——–是否输出值为null的字段,默认为false
WriteNullNumberAsZero—-数值字段如果为null,输出为0,而非null
WriteNullListAsEmpty—–List字段如果为null,输出为[],而非null
WriteNullStringAsEmpty—字符类型字段如果为null,输出为”“,而非null
WriteNullBooleanAsFalse–Boolean字段如果为null,输出为false,而非null
//修改代码,加入SerializerFeature,根据具体可能为null的值类型来选择,我们这里输出null毫无意义
String str = JSONObject.toJSONString(jsonMap, SerializerFeature.WriteMapNullValue);
输出结果:
参考链接:
SpringMVC返回JSON数据以及文件上传、过滤静态资源