springboot 全局配置时间格式化
在web
项目中,后台服务返回给前端的时间格式若未配置,则返回的数据可能是时间戳或者数组,例子如下:
entity 类型
LocalDateTime
{
"id": 5,
"name": null,
"age": null,
"jsonStr": null,
"ct": "2020-08-04T14:56:54.971"
}
entity 类型为
Date
,时间为时间戳
{
"id": 5,
"ct": 1596524214971
}
若需要显示yyyy-MM-dd HH:mm:ss
时间格式时,可以在对应的属性上配置@JsonFormat
注解:例如:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date ct;
还可以支持
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime ct;
若系统内时间属性字段相对多了之后,会多次配置,也会比较麻烦,所以需要进行全局配置
若时间属性字段为Date
类型时,可直接在配置文件中配置如下配置:
spring.jackson.date-formate=yyyy-MM-dd HH:mm:ss
若时间属性为JDK1.8以后的接口时,此配置就不生效了。为解决这个查看了spring-boot的实现方式。
spring-boot提供了JacksonAutoConfiguration
自动配置相关类,观察源码:
private void configureDateFormat(Jackson2ObjectMapperBuilder builder) {
String dateFormat = this.jacksonProperties.getDateFormat();
if(dateFormat != null) {
try {
Class ex = ClassUtils.forName(dateFormat, (ClassLoader)null);
builder.dateFormat((DateFormat)BeanUtils.instantiateClass(ex));
} catch (ClassNotFoundException var6) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
TimeZone timeZone = this.jacksonProperties.getTimeZone();
if(timeZone == null) {
timeZone = (new ObjectMapper()).getSerializationConfig().getTimeZone();
}
simpleDateFormat.setTimeZone(timeZone);
builder.dateFormat(simpleDateFormat);
}
}
}
@ConfigurationProperties(
prefix = "spring.jackson"
)
public class JacksonProperties {
private String dateFormat;
}
查看源码可知,当配置spring.jackson.date-formate
属性时,若配置为类路径,则初始化格式化类,进行处理。若配置的不为类路径时,采用SimpleDateFormat
类进行格式化时间,而SimpleDateFormat
这个类只能对Date
类型的时间格式化,所以当采用JDK1.8版本以后的时间接口时,会出现配置失效的问题。
当查看JacksonAutoConfiguration
源码时:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({ObjectMapper.class})
public class JacksonAutoConfiguration {
...
}
JacksonAutoConfiguration
类配置了条件注解 @ConditionalOnClass
依赖ObjectMapper
。
查看ObjectMapper
源码:
public ObjectMapper registerModule(Module module) {
this._assertNotNull("module", module);
String name = module.getModuleName();
if(name == null) {
throw new IllegalArgumentException("Module without defined name");
} else {
Version version = module.version();
if(version == null) {
throw new IllegalArgumentException("Module without defined version");
} else {
...
module.setupModule(new SetupContext() {
...
//反序列化
public void addDeserializers(Deserializers d) {
DeserializerFactory df = ObjectMapper.this._deserializationContext._factory.withAdditionalDeserializers(d);
ObjectMapper.this._deserializationContext = ObjectMapper.this._deserializationContext.with(df);
}
...
//序列化
public void addSerializers(Serializers s) {
ObjectMapper.this._serializerFactory = ObjectMapper.this._serializerFactory.withAdditionalSerializers(s);
}
...
});
return this;
}
}
}
由源码可知,ObjectMapper
类在此处定义了时间序列化相关配置,查看Module
实现类JavaTimeModule
:
public JavaTimeModule() {
super(PackageVersion.VERSION);
...
this.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
this.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
this.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);
...
this.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE);
this.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE);
this.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE);
...
this.addKeyDeserializer(LocalDateTime.class, LocalDateTimeKeyDeserializer.INSTANCE);
this.addKeyDeserializer(LocalDate.class, LocalDateKeyDeserializer.INSTANCE);
this.addKeyDeserializer(LocalTime.class, LocalTimeKeyDeserializer.INSTANCE);
...
}
JavaTimeModule
设置了LocalDateTime
,LocalDate
,LocalTime
的默认的序列化和反序列的实现。
springboot
提供了自定义ObjectMapper
/**
* 更改jackson默认配置
* 支持 Date和jdk8的时间操作类
* 配置时间格式化的全局操作,允许配置优先级更高的注解@JsonFormat 当配置@JsonFormat注解时以@JsonFormat配置为主
*/
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 对于空的对象转json的时候不抛出错误
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 禁用遇到未知属性抛出异常
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 序列化BigDecimal时不使用科学计数法输出
objectMapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
//null的属性不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 日期和时间格式化
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
备注:虽说配置了自定义的
ObjectMapper
,但依旧只是支持LocalDateTime
,LocalDate
,LocalTime
类相关的配置,若系统内部还是采用Date
类型,只需在配置文件中添加spring.jackson.date-formate=yyyy-MM-dd HH:mm:ss
若需要支持
LocalDateTime
,LocalDate
,LocalTime
自定义配置,一定不能使用@EnableWebMvc
注解。否则自定义配置ObjectMapper
会失效。{ "id": 5, "name": null, "age": null, "jsonStr": null, "ct": [ 2020, 8, 4, 14, 56, 54, 971000000 ] }
自此常用类型的时间在web项目中有服务器返回给前端的的时间格式化可按照自定义格式化处理.