点击 直接查看结论
后端接收国际标准时间格式 yyyy-MM-ddTHH:mm:ss.SSSZ(eg: 2018-01-13T16:00:00.000Z) 的时间格式,要转换成东八区时间,也就是北京时间。我根据经验用如下这种常见的方式处理:
@Configuration
public class CusLocalDateTimeConfig {
static final String PATTERN_UTC = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
static final String UTC_TAIL = "Z";
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
builder.deserializerByType(LocalDateTime.class, CusLocalDateTimeDeserializer.CUS_INSTANCE);
return builder.createXmlMapper(false).build();
}
/**
* 继承默认的 LocalDateTimeDeserializer 重写反序列方法
*/
public static class CusLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
public static final CusLocalDateTimeDeserializer CUS_INSTANCE = new CusLocalDateTimeDeserializer(DateTimeFormatter.ofPattern(PATTERN_UTC));
public CusLocalDateTimeDeserializer(DateTimeFormatter formatter) {
super(formatter);
}
@Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
String timeStr = parser.getText();
if (timeStr != null && !"".equals(timeStr)) {
if (timeStr.endsWith(UTC_TAIL)) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_UTC);
return LocalDateTime.parse(timeStr, formatter).plusHours(8);
}
}
return super.deserialize(parser, context);
}
}
}
通过Jackson2ObjectMapperBuilder创建一个 ObjectMapper,指定对LocalDateTime的反序列化处理类为我自定义的处理类。
可是,执行了一下,压根没执行我自定义的这个反序列方法。网上搜了一下也没有找到相关问题。
所以决定看看源码找找问题:
因为我对springmvc大体流程还是比较了解,我后台controller是用的 @RequestBody 接收参数,所以参数处理类是RequestResponseBodyMethodProcessor 这个类的这个方法
跟着断点依次执行到这里
经过几次断点调试后发现,这个body就是完成参数处理后的对象,所以处理参数的就是messageConverters中的某一个类
再次断点发现是如下处理类
然后搜了一下,发现在springboot的自动配置类里面
通过ObjectMapper创建一个MappingJackson2HttpMessageConverter类型的bean
所以我之前的做法的完全正确的,但是通过ConditionalOnMissingBean我猜测,也许是项目里面已经定义了这样的Bean我全局搜了一下,果然在某个包中发现了这样一个东西。
ps:此功能主要是因为js的精度问题 序列化Long型为字符串
所以我就把上面的内容加到这个地方,问题就解决了。
当然中途还发现了很多框架中其他的有意思的操作,就不细述
LocalDateTime 自定义时区转换的一种解决方式 : 全局反序列化处理 (此处以时间问题为例,当然也可以做其它任意操作)
public void save(@RequestBody Entity param)
第一种情况,项目中没使用自定义MappingJackson2HttpMessageConverter类型Bean的情况,可以直接创建一个ObjectMapper的Bean,指定builder中的反序列化类及对应类型
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
builder.deserializerByType(LocalDateTime.class, CusLocalDateTimeDeserializer.CUS_INSTANCE);
return builder.createXmlMapper(false).build();
}
/**
* 自定义反序列化类
* 继承默认的 LocalDateTimeDeserializer 重写反序列方法
*/
public static class CusLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
public static final CusLocalDateTimeDeserializer CUS_INSTANCE = new CusLocalDateTimeDeserializer(DateTimeFormatter.ofPattern(PATTERN_UTC));
public CusLocalDateTimeDeserializer(DateTimeFormatter formatter) {
super(formatter);
}
@Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
String timeStr = parser.getText();
if (timeStr != null && !"".equals(timeStr)) {
if (timeStr.endsWith(UTC_TAIL)) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_UTC);
return LocalDateTime.parse(timeStr, formatter).plusHours(8);
}
}
return super.deserialize(parser, context);
}
}
第二种情况,自定义MappingJackson2HttpMessageConverter类型的bean,(如果存在多个Bean,先加载的先执行,后加载的就不会再执行。可以通过@Order控制。但是注意千万别随意这样做) 在其他人也定义了Bean的时候,最好还是把功能放在同一个mappingJackson2HttpMessageConverter。以免导致其他功能失效
//在此之前应该先检查项目中有没有其他人创建MappingJackson2HttpMessageConverter类型的Bean
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDateTime.class, CusLocalDateTimeDeserializer.CUS_INSTANCE);
objectMapper.registerModule(javaTimeModule);
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
return mappingJackson2HttpMessageConverter;
}
public void save(Entity param)
可以实现 GenericConverter 或者 ConditionalGenericConverter
/**
* 自定义字符串到LocalDateTime的普通参数转换
*
**/
@Component
public class StringToLocalDateTimeConverter implements GenericConverter {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
//将字符串转化为LocalDateTime
return Collections.singleton(new ConvertiblePair(String.class, LocalDateTime.class));
}
//具体转换方法
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (((String) source).endsWith("Z")) {
return stringToLocalDateTimeUtc((String) source);
}
return stringToLocalDateTimeGeneral((String) source);
}
}
ConditionalGenericConverter ,他多提供了一个是否采用此方法的判断