背景
- 如今炒的火热的前后端分离项目,大多数开发人员选择RESTful设计风格,Java Web 人员经常要设计 RESTful API,这种设计通常使用 json 数据进行交互。那么前端传入的 json 数据如何序列化成 Java 对象,后端返回的结果又如何将Java 对象解析成 json 格式数据返回给前端。
spring-boot-stater-web 依赖的json解析是jackson ,同时也为我们进行了jackson的一系列自动化配置,这样我们不需要导入其他json依赖,就可以直接使用,其中起到关键作用的是MappingJackson2HttpMessageConverter - 在整个解析过程,HttpMessageConverter 起到了重要作用,各大公司实现了自己的HttpMessageConverter,如:
jackson的MappingJackson2HttpMessageConverter; gson的GsonHttpMessageConverter; fastjson的FastJsonHttpMessageConverter等等。
问题
数据库数据类型datetime ,java类型为LocalDateTime ,我们来看看springboot默认返回什么时间格式给前端
可以看到,是世界时间格式,因为LocalDateTime类型是和时区有关的时间,"2022-03-07T15:09:19"格式,T就是表示UTC 的世界时间,但是我们并不需要这种格式,我们不想把T返回或者我们只需要返回日期,怎么办呢
源码分析
刚刚我们讲到spring-boot-stater-web 依赖的json解析是jackson ,其中起到关键作用的是MappingJackson2HttpMessageConverter
@Configuration
@ConditionalOnClass({ObjectMapper.class})
@ConditionalOnBean({ObjectMapper.class})
@ConditionalOnProperty(
name = {"spring.http.converters.preferred-json-mapper"},
havingValue = "jackson",
matchIfMissing = true
)
protected static class MappingJackson2HttpMessageConverterConfiguration {
protected MappingJackson2HttpMessageConverterConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
value = {MappingJackson2HttpMessageConverter.class},
ignoredType = {"org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter", "org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter"}
)
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
return new MappingJackson2HttpMessageConverter(objectMapper);
}
}
可以看到这里做了MappingJackson2HttpMessageConverter 类的bean声明,会依赖ObjectMapper 注入
/**
* Construct a new {@link MappingJackson2HttpMessageConverter} with a custom {@link ObjectMapper}.
* You can use {@link Jackson2ObjectMapperBuilder} to build it easily.
* @see Jackson2ObjectMapperBuilder
*/
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}
我们看下MappingJackson2HttpMessageConverter 的构造方法,为json类型设置ObjectMapper 映射关系,可以自定义ObjectMapper ,可以通过Jackson2ObjectMapperBuilder 快速构建ObjectMapper
我们来看下Jackson2ObjectMapperBuilder
构建常规 JSON {@link ObjectMapper} 实例。
/**
* Obtain a {@link Jackson2ObjectMapperBuilder} instance in order to
* build a regular JSON {@link ObjectMapper} instance.
*/
public static Jackson2ObjectMapperBuilder json() {
return new Jackson2ObjectMapperBuilder();
}
自定义反序列化规则
/**
* Configure custom deserializers. Each deserializer is registered for the type
* returned by {@link JsonDeserializer
* @since 4.3
* @see
*/
public Jackson2ObjectMapperBuilder deserializers(JsonDeserializer<?>... deserializers) {
for (JsonDeserializer<?> deserializer : deserializers) {
Class<?> handledType = deserializer.handledType();
if (handledType == null || handledType == Object.class) {
throw new IllegalArgumentException("Unknown handled type in " + deserializer.getClass().getName());
}
this.deserializers.put(deserializer.handledType(), deserializer);
}
return this;
}
自定义序列化规则
/**
* Configure custom serializers. Each serializer is registered for the type
* returned by {@link JsonSerializer
* @see
*/
public Jackson2ObjectMapperBuilder serializers(JsonSerializer<?>... serializers) {
for (JsonSerializer<?> serializer : serializers) {
Class<?> handledType = serializer.handledType();
if (handledType == null || handledType == Object.class) {
throw new IllegalArgumentException("Unknown handled type in " + serializer.getClass().getName());
}
this.serializers.put(serializer.handledType(), serializer);
}
return this;
}
构建ObjectMapper ,并设置相关规则
/**
* Build a new {@link ObjectMapper} instance.
* <p>Each build operation produces an independent {@link ObjectMapper} instance.
* The builder's settings can get modified, with a subsequent build operation
* then producing a new {@link ObjectMapper} based on the most recent settings.
* @return the newly built ObjectMapper
*/
@SuppressWarnings("unchecked")
public <T extends ObjectMapper> T build() {
ObjectMapper mapper;
if (this.createXmlMapper) {
mapper = (this.defaultUseWrapper != null ?
new XmlObjectMapperInitializer().create(this.defaultUseWrapper, this.factory) :
new XmlObjectMapperInitializer().create(this.factory));
}
else {
mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper());
}
configure(mapper);
return (T) mapper;
}
全局配置方式
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
@Configuration
public class ObjectMapperConfig {
@Bean
public ObjectMapper jsonObjectMapper(){
Jackson2ObjectMapperBuilder objectMapperBuilder = Jackson2ObjectMapperBuilder.json();
//objectMapperBuilder.serializers(new CustomDateSerialize());
objectMapperBuilder.serializers(new CustomLocalDateTimeSerialize());
return objectMapperBuilder.build();
}
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class CustomLocalDateTimeSerialize extends JsonSerializer<LocalDateTime> {
private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
private DateTimeFormatter sdf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneId.systemDefault());
public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
String localTime = df.format(localDateTime);
jsonGenerator.writeString(localTime);
}
@Override
public Class<LocalDateTime> handledType() {
return LocalDateTime.class;
}
}
我们看看自定义序列化后的结果
可以看到返回前端的是序列化后的格式,其他类型如Date ,ZonedDateTime 等等都可以使用这种方式序列化和反序列化
|