我们知道,SpringMVC 可以接收 JSON 参数,也可以返回 JSON 参数,这一切依赖于HttpMessageConverter
, HttpMessageConverter
可以将一个 JSON 字符串转为对象,也可以将一个对象转为 JSON 字符串,实际上它的底层还是依赖于具体的 JSON 库,所有的 JSON 库要在 SpringMVC 中自动返回或者接收 JSON ,都必须提供和自己相关的 HttpMessageConverter
。
SpringMVC 中,默认提供了 Jackson
和 gson
的 HttpMessageConverter
,分别是: MappingJackson2HttpMessageConverter
和 GsonHttpMessageConverten
,正因为如此,我们在 SpringMVC 中,如果要使用 JSON ,对于 jackson 和 gson 我们只需要添加依赖,加完依赖就可以直接使用了。具体的配量是在 AllEncompassingFormHttpMessageConverter
类中完成的。
Spring Boot 对于 SpringMVC 一脉相承,所有都继承下来了,而且,只要我们在 Spring Boot 项目中,引入spring-boot-starter-web
这个依赖,就自动的加入了 Jackson
的依赖,所以一般来说,如果不是非常特殊的需求,建议在 Spring Boot 中直接使用 jackson
。
使用 Jsckson
创建 Controller
用于测试返回的 JSON 数据,代码如下:
@RestController
public class BookController {
@Autowired
Book book;
@GetMapping("/book")
public Book book() {
return book;
}
@PostMapping("/book")
public Book book(@RequestBody Book book) {
return book;
}
}
@Component
@ConfigurationProperties(prefix = "book")
public class Book {
private Integer id;
private String name;
private String author;
//此处略过 构造函数,get 和 set 方法
}
GET 请求
application.properties
配置如下:
book.id=1
book.name=三国演义
book.author=罗贯中
启动项目,使用application.properties
配置文件获取数据,访问:http://localhost:8080/book
Post 请求
这里 Post 请求,小编就不写前端页面了,这里小编就使用 Postman 工具进行测试:
使用 gson
如果有想使用 gson 的朋友,只需要把 Jackson 的依赖排除,然后添加 gson 的依赖即可,因为 Spring Boot 中已经提供了 gson
的 HttpMessageConverter
所以我们只需要换一下依赖即可使用,不需要任何配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除 jsckson 的依赖-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加 gson 的依赖-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
启动项目,访问 http://localhost:8080/book 结果和 之前一样,只不过使用的依赖不同了,效果还是一样的:
使用 fastjson
如果想在 Spring Boot 中引入 fastjson,则需要自己配置 HttpMessageConverter
。
首先我们同样需要排除 jackson 依赖,引入 fastjson 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除 jsckson 的依赖-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加 fastjson 依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
提供一个 fastjson 的转换器
提供 fastjson 的 HttpMessageConverter
一共有两种方式,第一种方式,就是在已有的转换器集合上添加 fastjson 的转换器,这种方式就是要实现 WebMvcConfigurer 这个接口中的 configureMessageConverters 方法:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
converters.add(converter);
}
}
当我们启动项目发现数据乱码,后台报错:
Caused by: java.lang.IllegalArgumentException: Content-Type cannot contain wildcard type '*'
出现这种情况可能是版本的问题,目前我使用的 fastjson 是最新的版本,之前的版本是不会出现这种情况的,解决,我们需要在我们自己提供的 fastjson 的 HttpMessageConverter
添加如下代码:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> types = new ArrayList<>();
types.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(types);
converters.add(converter);
}
}
再次启动项目访问:http://localhost:8080/book ,正常了。
第二种方式,我们不实现接口的方法,我们自己直接提供一个 FastJsonHttpMessageConverter
:
@Configuration
public class WebMvcConfig {
@Bean
FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> types = new ArrayList<>();
types.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(types);
return converter;
}
}
启动项目,访问:http://localhost:8080/book
能够访问成功,说明第二种方式也是可以的。
JSON 传输过程:
由于 Jackson 的 HttpMessageConverter
是在 AllEncompassingFormHttpMessageConverter
类中初始化的,在该类的无参构造函数中,会根据当前的条件去判断要加载哪个一个转换器,对于 Jackson 相关依赖,当前类路径下就会存在 ObjectMapper 和 JsonGenerator,进而使得 jackson2Present 变量为 true,然后在构造函数中就会添加 MappingJackson2HttpMessageConverter
转换器,这就是 jackson 的 HttpMessageConverter
。
部分源码:
public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConverter {
private static final boolean jackson2Present;
private static final boolean gsonPresent;
static {
jackson2Present=ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",classLoader)&&ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
}
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
addPartConverter(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
addPartConverter(new JsonbHttpMessageConverter());
}
}
我们从这段代码中可以看出来,如果同时存在 jackson 和 gson 的话,会优先使用 jackson。