--- title: SpringBoot 之集成 Json date: 2018-12-30 22:24:16 order: 02 categories: - Java - 框架 - Spring - SpringIO tags: - Java - 框架 - Spring - SpringBoot - JSON permalink: /pages/f7d8c4ce/ --- # SpringBoot 之集成 Json ## 简介 ### Spring Boot 支持的 Json 库 Spring Boot 支持三种 Json 库: - Gson - Jackson - JSON-B **Jackson 是 Spring Boot 官方推荐的默认库。** Spring Boot 提供了 Jackson 的自动配置,Jackson 是 `spring-boot-starter-json` 的一部分。当 Jackson 在类路径上时,会自动配置 ObjectMapper bean。 Spring Boot 提供了 Gson 的自动配置。当 Gson 在 classpath 上时,会自动配置 Gson bean。提供了几个 `spring.gson.*` 配置属性来自定义配置。为了获得更多控制,可以使用一个或多个 `GsonBuilderCustomizer` bean。 Spring Boot 提供了 JSON-B 的自动配置。当 JSON-B API 在 classpath 上时,将自动配置 Jsonb bean。首选的 JSON-B 实现是 Apache Johnzon,它提供了依赖关系管理。 ### Spring Web 中的序列化、反序列化 以下注解都是 `spring-web` 中提供的支持。 #### `@ResponseBody` `@Responsebody` 注解用于将 Controller 的方法返回的对象,通过适当的 `HttpMessageConverter` 转换为指定格式后,写入到 HTTP Response 对象的 body 数据区。一般在异步获取数据时使用。通常是在使用 `@RequestMapping` 后,返回值通常解析为跳转路径,加上 @Responsebody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP 响应正文中。 示例: ```java @ResponseBody @RequestMapping(name = "/getInfo", method = RequestMethod.GET) public InfoDTO getInfo() { return new InfoDTO(); } ``` #### `@RequestBody` @RequestBody 注解用于读取 HTTP Request 请求的 body 部分数据,使用系统默认配置的 `HttpMessageConverter` 进行解析,然后把相应的数据绑定到要返回的对象上;再把 `HttpMessageConverter` 返回的对象数据绑定到 controller 中方法的参数上。 request 的 body 部分的数据编码格式由 header 部分的 `Content-Type` 指定。 示例: ```java @RequestMapping(name = "/postInfo", method = RequestMethod.POST) public void postInfo(@RequestBody InfoDTO infoDTO) { // ... } ``` #### `@RestController` Spring 4 以前: 如果需要返回到指定页面,则需要用 `@Controller` 配合视图解析器 `InternalResourceViewResolver` 。 如果需要返回 JSON,XML 或自定义 mediaType 内容到页面,则需要在对应的方法上加上 `@ResponseBody` 注解。 Spring 4 以后,新增了 `@RestController` 注解: 它相当于 `@Controller` + `@RequestBody` 。 如果使用 `@RestController` 注解 Controller,则 Controller 中的方法无法返回 jsp 页面,或者 html,配置的视图解析器 `InternalResourceViewResolver` 将不起作用,直接返回内容。 ## 指定类的 Json 序列化、反序列化 如果使用 Jackson 序列化和反序列化 JSON 数据,您可能需要编写自己的 `JsonSerializer` 和 `JsonDeserializer` 类。自定义序列化程序通常通过模块向 Jackson 注册,但 Spring Boot 提供了另一种 `@JsonComponent` 注释,可以更容易地直接注册 Spring Beans。 您可以直接在 `JsonSerializer` 或 `JsonDeserializer` 实现上使用 `@JsonComponent` 注释。您还可以在包含序列化程序/反序列化程序作为内部类的类上使用它,如以下示例所示: ```java import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import org.springframework.boot.jackson.*; @JsonComponent public class Example { public static class Serializer extends JsonSerializer { // ... } public static class Deserializer extends JsonDeserializer { // ... } } ``` `ApplicationContext` 中的所有 `@JsonComponent` bean 都会自动注册到 Jackson。因为 `@JsonComponent` 是使用 `@Component` 进行元注释的,所以通常的组件扫描规则适用。 Spring Boot 还提供了 [`JsonObjectSerializer`](https://github.com/spring-projects/spring-boot/tree/v2.1.1.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonObjectSerializer.java) 和 [`JsonObjectDeserializer`](https://github.com/spring-projects/spring-boot/tree/v2.1.1.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonObjectDeserializer.java) 基类,它们在序列化对象时提供了标准 Jackson 版本的有用替代方法。有关详细信息,请参阅 Javadoc 中的 [`JsonObjectSerializer`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/jackson/JsonObjectSerializer.html) 和 [`JsonObjectDeserializer`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/jackson/JsonObjectDeserializer.html)。 ## @JsonTest 使用 `@JsonTest` 可以很方便的在 Spring Boot 中测试序列化、反序列化。 使用 `@JsonTest` 相当于使用以下自动配置: ``` org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration ``` `@JsonTest` 使用示例: 想试试完整示例,可以参考:[源码](https://github.com/dunwu/spring-boot-tutorial/tree/master/codes/spring-boot-web-fastjson) ```java @JsonTest @RunWith(SpringRunner.class) public class SimpleJsonTest { private final Logger log = LoggerFactory.getLogger(this.getClass()); @Autowired private JacksonTester json; @Test public void testSerialize() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); InfoDTO infoDTO = new InfoDTO("JSON测试应用", "1.0.0", sdf.parse("2019-01-01 12:00:00")); JsonContent jsonContent = json.write(infoDTO); log.info("json content: {}", jsonContent.getJson()); // 或者使用基于JSON path的校验 assertThat(jsonContent).hasJsonPathStringValue("@.appName"); assertThat(jsonContent).extractingJsonPathStringValue("@.appName").isEqualTo("JSON测试应用"); assertThat(jsonContent).hasJsonPathStringValue("@.version"); assertThat(jsonContent).extractingJsonPathStringValue("@.version").isEqualTo("1.0.0"); assertThat(jsonContent).hasJsonPathStringValue("@.date"); assertThat(jsonContent).extractingJsonPathStringValue("@.date").isEqualTo("2019-01-01 12:00:00"); } @Test public void testDeserialize() throws Exception { String content = "{\"appName\":\"JSON测试应用\",\"version\":\"1.0.0\",\"date\":\"2019-01-01\"}"; InfoDTO actual = json.parseObject(content); assertThat(actual.getAppName()).isEqualTo("JSON测试应用"); assertThat(actual.getVersion()).isEqualTo("1.0.0"); } } ``` ## Spring Boot 中的 json 配置 ### Jackson 配置 当 Spring Boot 的 json 库为 jackson 时,可以使用以下配置属性(对应 [`JacksonProperties`](https://github.com/spring-projects/spring-boot/tree/v2.1.1.RELEASE/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java) 类): ```properties spring.jackson.date-format= # Date format string or a fully-qualified date format class name. For instance, `yyyy-MM-dd HH:mm:ss`. spring.jackson.default-property-inclusion= # Controls the inclusion of properties during serialization. Configured with one of the values in Jackson's JsonInclude.Include enumeration. spring.jackson.deserialization.*= # Jackson on/off features that affect the way Java objects are deserialized. spring.jackson.generator.*= # Jackson on/off features for generators. spring.jackson.joda-date-time-format= # Joda date time format string. If not configured, "date-format" is used as a fallback if it is configured with a format string. spring.jackson.locale= # Locale used for formatting. spring.jackson.mapper.*= # Jackson general purpose on/off features. spring.jackson.parser.*= # Jackson on/off features for parsers. spring.jackson.property-naming-strategy= # One of the constants on Jackson's PropertyNamingStrategy. Can also be a fully-qualified class name of a PropertyNamingStrategy subclass. spring.jackson.serialization.*= # Jackson on/off features that affect the way Java objects are serialized. spring.jackson.time-zone= # Time zone used when formatting dates. For instance, "America/Los_Angeles" or "GMT+10". spring.jackson.visibility.*= # Jackson visibility thresholds that can be used to limit which methods (and fields) are auto-detected. ``` ### GSON 配置 当 Spring Boot 的 json 库为 gson 时,可以使用以下配置属性(对应 [`GsonProperties`](https://github.com/spring-projects/spring-boot/tree/v2.1.1.RELEASE/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/gson/GsonProperties.java) 类): ```properties spring.gson.date-format= # Format to use when serializing Date objects. spring.gson.disable-html-escaping= # Whether to disable the escaping of HTML characters such as '<', '>', etc. spring.gson.disable-inner-class-serialization= # Whether to exclude inner classes during serialization. spring.gson.enable-complex-map-key-serialization= # Whether to enable serialization of complex map keys (i.e. non-primitives). spring.gson.exclude-fields-without-expose-annotation= # Whether to exclude all fields from consideration for serialization or deserialization that do not have the "Expose" annotation. spring.gson.field-naming-policy= # Naming policy that should be applied to an object's field during serialization and deserialization. spring.gson.generate-non-executable-json= # Whether to generate non executable JSON by prefixing the output with some special text. spring.gson.lenient= # Whether to be lenient about parsing JSON that doesn't conform to RFC 4627. spring.gson.long-serialization-policy= # Serialization policy for Long and long types. spring.gson.pretty-printing= # Whether to output serialized JSON that fits in a page for pretty printing. spring.gson.serialize-nulls= # Whether to serialize null fields. ``` ## Spring Boot 中使用 Fastjson 国内很多的 Java 程序员更喜欢使用阿里的 fastjson 作为 json lib。那么,如何在 Spring Boot 中将其替换默认的 jackson 库呢? 你需要做如下处理: (1)引入 fastjson jar 包: ```xml com.alibaba fastjson 1.2.54 ``` (2)实现 WebMvcConfigurer 接口,自定义 `configureMessageConverters` 接口。如下所示: ```java @Configuration public class WebMvcConfig implements WebMvcConfigurer { private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * 自定义消息转换器 * @param converters */ @Override public void configureMessageConverters(List> converters) { // 清除默认 Json 转换器 converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter); // 配置 FastJson FastJsonConfig config = new FastJsonConfig(); config.setSerializerFeatures(SerializerFeature.QuoteFieldNames, SerializerFeature.WriteEnumUsingToString, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.DisableCircularReferenceDetect); // 添加 FastJsonHttpMessageConverter FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); fastJsonHttpMessageConverter.setFastJsonConfig(config); List fastMediaTypes = new ArrayList<>(); fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes); converters.add(fastJsonHttpMessageConverter); // 添加 StringHttpMessageConverter,解决中文乱码问题 StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8")); converters.add(stringHttpMessageConverter); } // ... } ``` ## 示例源码 完整示例:[源码](https://github.com/dunwu/spring-boot-tutorial/tree/master/codes/spring-boot-web-fastjson) ## 引申和引用 **引申** - [Spring Boot 教程](https://github.com/dunwu/spring-boot-tutorial) **引用** - [Spring Boot 官方文档之 boot-features-json](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-json)