关于背景的大概描述,如下。
时间类型LocalDateTime相较于于Date的确有自己独特的优势,因此在之后的版本迭代上,将部分隔离开的模块使用了新型的LocalDateTime替代了Date,因已经使用了全局时间转换,但是测试的时候发现返回的时间格式很奇怪 2022-01-01T00:00:00.000,全局时间序列化、反序列化并没有起作用,寻找相关资料和DEGBU之后将相关资料整理如下。
目前常用的时间类型格式如下:
时间类型
时间默认格式
Date
Tue Feb 01 00:00:00 CST 2022
2022-01-01 00:00:00.0
LocalDateTime
2022-01-01T00:00:00.000
对于Date和Timestamp在引入LocalDateTime之前是经过spring.jackson.date-format进行全局数据绑定格式化时间类型。
spring:
jackson:
date-format: 格式化类
在格式化类中添加对LocalDateTime的格式化语句发现无效果。经过搜索资料后发现在使用jackson在对localDateTime类型的序列化和反序列化方法另有其他方法。(这里不输出关于源码内的信息。可自行查看。)
// 涉及源码类 其余的可自行debug追踪
Jackson2ObjectMapperBuilder
Jackson2ObjectMapperBuilderCustomizer // 最终做处理的服务类
JacksonAutoConfiguration
@Configuration
public class LocalDateTimeSerializerConfig {
// jackson注册的时候会将序列化和反序列化的方法提前注册进,做数据转换会自行根据具体的实现去调用。
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer());
};
}
/**
* 反序列化
*/
public static class LocalDateTimeDeserializer extends JsonDeserializer {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
throws IOException {
long timestamp = p.getValueAsLong();
if (timestamp > 0) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
} else {
return null;
}
}
}
}
使用fastjson进行时间格式化处理,自己写的类去实现 WebMvcConfigurer配置类,重写父类方法configureMessageConverters,在进行全局时间返回格式绑定后,之前使用的格式化时间类是fastjson自身提供的SimpleDateFormatSerializer方法,发现在进行对LocalDateTime添加后进行时间转化返回后存在异常,异常信息提示LocalDateTime不能强转为Date,进行多次DEBUG跟踪后定位到了SimpleDateFormatSerializer方法内部,会将拿到的value直接进行Date的强转,SimpleDateFormatSerializer源码如下
public class SimpleDateFormatSerializer implements ObjectSerializer {
private final String pattern;
public SimpleDateFormatSerializer(String pattern){
this.pattern = pattern;
}
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
if (object == null) {
serializer.out.writeNull();
return;
}
// 对LocalDateTime 进行强制转换,此处会报错。因此,主要的重点改造就是此处。
Date date = (Date) object;
SimpleDateFormat format = new SimpleDateFormat(pattern, serializer.locale);
format.setTimeZone(serializer.timeZone);
String text = format.format(date);
serializer.write(text);
}
}
既然已经发现问题的关键点那对关键点进行处理即可,目前有两个方法:
我选用的是第一种方式对SimpleDateFormatSerializer集成后重写write方法,因为之前的时间格式已经使用的SimpleDateFormatSerializer进行处理,为了稳定进行最小程度的调整。结果如下
public class OwnSimpleDateFormatSerializer extends SimpleDateFormatSerializer {
private final String pattern;
public OwnSimpleDateFormatSerializer (String pattern) {
super(pattern);
this.pattern = pattern;
}
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
if (object == null) {
serializer.out.writeNull();
} else {
Date date = null;
// 改造内容 增加类型判断进行处理
if (object instanceof LocalDateTime) {
LocalDateTime localDateTime = (LocalDateTime) object;
date = Date.from(localDateTime.atZone( ZoneId.systemDefault()).toInstant());
} else {
date = (Date)object;
}
SimpleDateFormat format = new SimpleDateFormat(this.pattern, Locale.SIMPLIFIED_CHINESE);
TimeZone timeZone = new ZoneInfo();
timeZone.setRawOffset(28800000);
timeZone.setID("Asia/Shanghai");
format.setTimeZone(timeZone);
String text = format.format(date);
serializer.write(text);
}
}
}
// 涉及类
WebMvcConfigurer
SimpleDateFormatSerializer // 之前使用的时间格式化类
ObjectSerializer // 最终执行时间格式化处理的服务类
FastJsonConfig // 配置类
SerializeConfig // 序列化配置信息
@Component
public class DefaultMvcFilter implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List> converters) {
// ...
// 重点在此 对FastJsonConfig进行配置
FastJsonConfig fastJsonConfig = new FastJsonConfig();
// 修改全局json配置
SerializeConfig serializeConfig = SerializeConfig.globalInstance;
// 格式化时间
serializeConfig.put(Date.class, new OwnSimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
serializeConfig.put(java.sql.Date.class, new OwnSimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
serializeConfig.put(java.sql.Timestamp.class, new OwnSimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
serializeConfig.put(java.sql.Time.class, new OwnSimpleDateFormatSerializer("HH:mm:ss"));
// 对LocalDateTime进行时间格式化 注意序列化的实现类 是需要自己写的,并不是使用fastjson自带的SimpleDateFormatSerializer
serializeConfig.put(LocalDateTime.class, new OwnSimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
fastJsonConfig.setSerializeConfig(serializeConfig);
fastJsonConfig.setSerializerFeatures(
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.IgnoreErrorGetter,
SerializerFeature.WriteNullNumberAsZero,
SerializerFeature.WriteNullBooleanAsFalse,
SerializerFeature.DisableCircularReferenceDetect
);
}
}
以上对于jackson和fastjson改造完成后进行测试,发现时间格式全部展示正确,以此来记录问题发生和处理方式。主要还是根据源码跟踪来确定问题,问题确定后的处理就会有很多方式了,选择最适用的处理方式即可。
先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦