当然可以
想想看我们在项目中什么时候会用到这两个注解呢?
- Get请求传入java.util.Date、java.time.xxx类型参数,需要配上@DateTimeFormat来解析
- Post请求传入json时候,同样需要在上述两种时间属性上加@JsonFormat来反序列化
- 返回对象里有java.util.Date需加@JsonFormat,配置好时区,不然时间会少8小时
先看看平常我们是怎么使用这两个注解的
@DateTimeFormat@GetMapping("test")
public Stu testGet(Stu stu) {
log.info("stu get: " + stu);
return stu;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Stu {
// 姓名
private String name;
// 生日
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime birth;
// 毕业时间
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date graduate;
}
这两个参数如果不加@DateTimeFormat注解的话,会报以下的错误
Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors ...省略一些日志... [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'birth'; ...省略一些日志... [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'graduate';
@JsonFormat
@PostMapping("test")
public Stu testPost(@RequestBody Stu stu) {
log.info("stu post: " + stu);
return stu;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Stu {
// 姓名
private String name;
// 生日
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime birth;
// 毕业时间
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date graduate;
}
如果不加@JsonFormat注解,则会报序列化错误
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: ...省略部分日志... Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) ...省略部分日志... JSON parse error: Cannot deserialize value of type `java.util.Date` from String "2029-01-01 10:00:00": not a valid representation (error: Failed to parse Date value '2029-01-01 10:00:00': Cannot parse date "2029-01-01 10:00:00":
所以有了这两个注解,就能很好解决项目开发中参数入参、出参的问题。
但是,很多地方使用了这些注解时候,就要想想有没有全局统一配置呢?
下面给出全局配置的代码
核心依赖定义一些常量和Converterorg.springframework.boot spring-boot-starter-web org.projectlombok lombok true cn.hutool hutool-all 5.6.6 commons-lang commons-lang 2.6
public class DateConfig {
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public static final String DEFAULT_YEAR_MONTH_FORMAT = "yyyy-MM";
static class LocalDateTimeConverter implements Converter {
@Override
public LocalDateTime convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
if (StringUtils.isNumeric(source)) {
return Instant.ofEpochMilli(Long.parseLong(source)).atZone(ZoneId.systemDefault()).toLocalDateTime();
} else {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT));
}
}
}
static class LocalDateConverter implements Converter {
@Override
public LocalDate convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
if (StringUtils.isNumeric(source)) {
return Instant.ofEpochMilli(Long.parseLong(source)).atZone(ZoneId.systemDefault()).toLocalDate();
} else {
return LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
}
}
}
static class LocalTimeConverter implements Converter {
@Override
public LocalTime convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
return LocalTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT));
}
}
static class YearMonthConverter implements Converter {
@Override
public YearMonth convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
return YearMonth.parse(source, DateTimeFormatter.ofPattern(DEFAULT_YEAR_MONTH_FORMAT));
}
}
static class DateConverter implements Converter {
@Override
public Date convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
if (StringUtils.isNumeric(source)) {
return new Date(Long.parseLong(source));
} else {
return DateUtil.parse(source);
}
}
}
}
配置入参解析
@Configuration
public class ServletContextConfig implements WebMvcConfigurer {
// 解析时间入参
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DateConfig.LocalDateConverter());
registry.addConverter(new DateConfig.LocalDateTimeConverter());
registry.addConverter(new DateConfig.YearMonthConverter());
registry.addConverter(new DateConfig.LocalTimeConverter());
registry.addConverter(new DateConfig.DateConverter());
}
}
自定义序列化和反序列器
@Component
public class MyJackson2ObjectMapperBuilderCustomizer implements Jackson2ObjectMapperBuilderCustomizer {
@Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
JavaTimeModule javaTimeModule = new JavaTimeModule();
// =========================================== 序列化器 =========================================================
// LocalDateTime序列化器
javaTimeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT)));
// LocalDateTime序列化器
javaTimeModule.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_FORMAT)));
// LocalTime序列化器
javaTimeModule.addSerializer(LocalTime.class,
new LocalTimeSerializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_TIME_FORMAT)));
// YearMonth序列化器
javaTimeModule.addSerializer(YearMonth.class,
new YearMonthSerializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_YEAR_MONTH_FORMAT)));
// Date序列化器
javaTimeModule.addSerializer(Date.class, new JsonSerializer() {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(DateUtil.format(date, DateConfig.DEFAULT_DATE_TIME_FORMAT));
}
});
// =========================================== 反序列化器 =======================================================
// LocalDateTime反序列化器
javaTimeModule.addDeserializer(LocalDateTime.class, new JsonDeserializer() {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String text = p.getText();
if (StringUtils.isEmpty(text)) {
return null;
}
if (StringUtils.isNumeric(text)) {
return Instant.ofEpochMilli(Long.parseLong(text)).atZone(ZoneId.systemDefault()).toLocalDateTime();
} else {
return LocalDateTime.parse(text, DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_TIME_FORMAT));
}
}
});
// LocalDate反序列化器
javaTimeModule.addDeserializer(LocalDate.class, new JsonDeserializer() {
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String text = p.getText();
if (StringUtils.isBlank(text)) {
return null;
}
if (StringUtils.isNumeric(text)) {
return Instant.ofEpochMilli(Long.parseLong(text)).atZone(ZoneId.systemDefault()).toLocalDate();
} else {
return LocalDate.parse(text, DateTimeFormatter.ofPattern(DateConfig.DEFAULT_DATE_FORMAT));
}
}
});
// LocalTime反序列化器
javaTimeModule.addDeserializer(LocalTime.class,
new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_TIME_FORMAT)));
// YearMonth反序列化器
javaTimeModule.addDeserializer(YearMonth.class,
new YearMonthDeserializer(DateTimeFormatter.ofPattern(DateConfig.DEFAULT_YEAR_MONTH_FORMAT)));
// Date反序列化器
javaTimeModule.addDeserializer(Date.class, new JsonDeserializer() {
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String text = p.getText();
if (StringUtils.isBlank(text)) {
return null;
}
if (StringUtils.isNumeric(text)) {
return new Date(Long.parseLong(text));
} else {
return DateUtil.parse(text);
}
}
});
// 添加进去
jacksonObjectMapperBuilder.modules(javaTimeModule);
}
}
现在可以愉快的开发啦 不用再担心日期时间不能被解析,也不用担心时区问题导致时间不准确的问题。



