【springMVC】全局json序列化配置
1.概述
现在我们进行web开发,一般都要设计成RESTful风格的API,通过json格式的数据进行交互。但是前端传入的 json 数据如何被解析成 Java 对象作为 API入参,后端返回结果又如何将 Java 对象解析成 json 格式数据返回给前端,在整个数据流转过程中,这是由谁来完成的呢?
SpringMVC源码中使用Jackson库或Gson库来转换json字符串,使用使用Jackson XML或者JAXB2来转换xml。
2.HttpMessageConverter简介
org.springframework.http.converter.HttpMessageConverter 是SpringMVC中提供的一个策略接口,它是一个转换器类,负责转换HTTP请求和响应,可以把对象自动转换为JSON(使用Jackson库或Gson库)或XML(使用Jackson XML或者JAXB2),对字符串默认使用UTF-8编码处理,一般情况下采用默认配置就可以。
而如果我们没有配置自己的 MessageConverter,SpringMVC 启动时就会调用 addDefaultHttpMessageConverters方法。
3.HttpMessageConverter转换原理
利用SpringMVC框架,可以使得我们在开发时,只要在代码中使用@RequestBody和@ResponseBody两个注解,就可以分别完成从请求报文到对象和从对象到响应报文的转换。而在源码内部,其实这种灵活的消息转换机制就是利用HttpMessageConverter来实现的。
自定义 HttpMessageConverter(一般不添加自定义,使用默认配置):
package com.ryxx.config; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.ser.std.DateSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.ryxx.util.JacksonUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.TimeZone; import java.util.function.Predicate; /** * ClassName:JacksonObjectMapperConfig <br/> * Function: 一般不添加该配置,使用默认配置. <br/> * Reason: 一般不添加该配置,使用默认配置. <br/> * Date: 2023/08/17/11:31 <br/> * * @author guocheng * @version 1.0 * @since JDK 1.8 */ @Configuration public class JacksonObjectMapperConfig extends WebMvcConfigurationSupport { @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss.SSS}") private String pattern; @Value("${spring.jackson.time-zone:GMT+8}") private String timeZoneString; /** * 重定制 objectMapper * 想完全控制序列化过程并且不想允许外部配置时打开 @Primary 注解 */ @Bean(name = "objectMapper") // @Primary public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); // POJO对象的属性值不论是什么,序列化时都显示 objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); // 设置在反序列化时忽略在JSON字符串中存在,而在Java中不存在的属性 objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 设置序列化时间类型不使用时间戳形式(jackson时间类型默认都是序列化为时间戳形式的) objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 为了处理JSON中的日期字符串并正确转换为 LocalDate,我们需要注册在 ObjectMapper 实例中注册 JavaTimeModule 模块。 // 还需要禁止把日期转换为时间戳字符串的动作 JavaTimeModule javaTimeModule = new JavaTimeModule(); // Date类型序列化、反序列化全局配置-方式1 // objectMapper.setDateFormat(new SimpleDateFormat(pattern)); // Date类型序列化、反序列化全局配置-方式2 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern); simpleDateFormat.setTimeZone(TimeZone.getTimeZone(timeZoneString)); javaTimeModule.addSerializer(Date.class, new DateSerializer(false, simpleDateFormat)); javaTimeModule.addDeserializer(Date.class, new JacksonUtils.DateDeserializer()); // LocalDateTime类型序列化、反序列化全局配置 javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern))); objectMapper.registerModule(javaTimeModule); return objectMapper; } /** * org.springframework.http.converter.HttpMessageConverter * 是SpringMVC中提供的一个策略接口,它是一个转换器类,负责转换HTTP请求和响应, * 可以把对象自动转换为JSON(使用Jackson库或Gson库)或XML(使用Jackson XML或者JAXB2), * 对字符串默认使用UTF-8编码处理,一般情况下采用默认配置就可以。 * <p> * 自定义 HttpMessageConverter * 所以如果想要覆盖 Spring MVC 中默认的 HttpMessageConverter,可以重写 #configureMessageConverters 方法, * 如果只是想在默认列表中添加自定义的 HttpMessageConverter 则可以重写 #extendMessageConverters 方法。 * <p> * HttpMessageConverter 同时支持请求体转换为类对象,以及将类对象转换为响应体, * 因此 RequestResponseBodyMethodProcessor 不管处理参数还是返回值都用到了它。 * RequestResponseBodyMethodProcessor 将请求体转换为 Controller 方法参数, * 将 Controller 方法返回值转换为响应体。 */ @Override protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 已有的MappingJackson2HttpMessageConverter bean有可能时区不是gmt+8,直接干掉,然后添加一个新的 converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance); // 将重定制 objectMapper 添加自定义的 HttpMessageConverter中, // 使 @requestBody 和 @ResponseBody 注解的序列化与反序列化生效 converters.add(new MappingJackson2HttpMessageConverter(objectMapper())); } /** * 假定我们的项目中引入了 fastjson,如果我们想要配置 fastjson * 自带的 HttpMessageConverter 处理 application/json 内容类型,只需如下的配置。 */ // @Override // protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); // converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON)); // converters.add(0, converter); // } }
重置 jackson 默认的 ObjectMapper(一般添加该配置即可):
package com.ryxx.config; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.deser.std.NumberDeserializers; import com.fasterxml.jackson.databind.ser.std.DateSerializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.boot.jackson.JsonComponent; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import java.io.IOException; import java.math.BigDecimal; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.TimeZone; /** * jackson全局时间格式化配置<br/> * springboot扫描到即生效<br/> * 优先级低于{@link JsonFormat}注解 * * @JsonComponent 注解的类会被 SpringBoot 自动注入到默认的 ObjectMapper 中, * 重新定制 ObjectMapper 会覆盖该配置。 */ @JsonComponent public class JsonDateFormatConfig { @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}") private String pattern; @Value("${spring.jackson.time-zone:Asia/Shanghai}") private String timeZoneString; @Bean @Primary public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return builder -> { builder.failOnEmptyBeans(false); builder.failOnUnknownProperties(false); // 要禁用的功能(将日期写成时间戳) builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // Date类型序列化配置 DateFormat df = new SimpleDateFormat(pattern); // 设置时区 df.setTimeZone(TimeZone.getTimeZone(timeZoneString)); // Date类型序列化、反序列化全局配置-方式1 builder.dateFormat(df); /* // Date类型序列化配置(不使用时间戳)-方式2 builder.serializerByType(Date.class, new DateSerializer(false, df)); // Date类型反序列化配置 builder.deserializerByType(Date.class, new JsonDeserializer<Date>() { @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String dateStr = p.getText().trim(); if ("".equals(dateStr)) { return null; } Date date = null; try { date = new SimpleDateFormat(pattern).parse(dateStr); } catch (ParseException e) { e.printStackTrace(); } return date; } });*/ // LocalDateTime类型序列化配置 builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern))); // LocalDateTime类型反序列化配置 builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern))); // BigDecimal类型反序列化配置 builder.deserializerByType(BigDecimal.class, new NumberDeserializers.BigDecimalDeserializer()); }; } // /** // * LocalDateTime序列化器 // */ // @Bean // public LocalDateTimeSerializer localDateTimeSerializer() { // return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern)); // } // // /** // * LocalDateTime反序列化器 // */ // @Bean // public LocalDateTimeDeserializer localDateTimeDeserializer() { // return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern)); // } }
参考:SpringBoot系列教程22-整合SpringMVC之HttpMessageConverters - 知乎 (zhihu.com)
SpringBoot系列教程22-整合SpringMVC之HttpMessageConverters - 知乎 (zhihu.com)
本文来自博客园,作者:日月星宿,转载请注明原文链接:https://www.cnblogs.com/ryxxtd/p/17640247.html