大部分人在二三十岁上就死去了,因为过了这个年龄,他们只是自己的影子,此后的余生则是在模仿自己中度过,日复一日,更机械,更装腔作势地重复他们在有生之年的所作所为,所思所想,所爱所恨。——《约翰 • 克利斯朵夫》
我们在开发中对于日期,如果是协商好,请求发送指定格式的日期字符串
我们这边再转换成对应日期格式,如果每个都一个一个来转,非常麻烦,不方便
这里介绍一种全局的mvc
转换方式
使用@InitBinder
注解以及Jackson2ObjectMapperBuilderCustomizer
如果我们使用application/x-www-form-urlencoded
接参,则@InitBinder
上场了
只需要在Controller
中写下如下代码
public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String DATE_PATTERN = "yyyy-MM-dd";
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
public static final ZoneId SYSTEM_DEFAULT_ZONE_ID = ZoneId.systemDefault();
/**
* 我还没有写描述
*
* @param binder 从Web请求参数到JavaBean对象的数据绑定的特殊DataBinder
* @author <achao1441470436@gmail.com>
* @date 2021/4/5 0005 0:48
*/
@InitBinder
protected void initBinder(WebDataBinder binder) {
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
@Override
@SneakyThrows
public void setAsText(String text) {
setValue(textToDate(text));
}
});
// LocalDate类型转换
binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
setValue(textToLocalDate(text));
}
});
// LocalDateTime类型转换
binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
setValue(textToLocalDateTime(text));
}
});
}
/**
* 时间字符串转Date
*
* @param text 2021-04-04 23:10:51
* @return java.util.Date|null
* @author <achao1441470436@gmail.com>
* @date 2021/4/4 0004 23:10
*/
private Date textToDate(String text) {
return Optional.ofNullable(textToLocalDateTime(text)).map(dateTime -> dateTime.atZone(SYSTEM_DEFAULT_ZONE_ID)).map(ZonedDateTime::toInstant).map(Date::from).orElse(null);
}
private LocalDate textToLocalDate(String text) {
return Optional.ofNullable(text).filter(str -> !str.isEmpty()).map(str -> LocalDate.parse(str, DATE_FORMATTER)).orElse(null);
}
/**
* 时间字符串转LocalDateTime
*
* @param text 2021-04-04 23:09:52
* @return java.time.ZonedDateTime|null
* @author <achao1441470436@gmail.com>
* @date 2021/4/4 0004 23:05
*/
private LocalDateTime textToLocalDateTime(String text) {
return Optional.ofNullable(text).filter(str -> !str.isEmpty()).map(str -> LocalDateTime.parse(str, DATE_TIME_FORMATTER)).orElse(null);
}
然后你当前Controller
中所有Date
格式或者LocalDateTime
格式的参数都可以使用yyyy-MM-dd
格式传参啦
我们用下面这个实体类OrderPO
@Data
@Builder
@TableName("`order`")
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class OrderPO implements Serializable {
private static final long serialVersionUID = 5494342842978779677L;
/**
* 编号
*/
@TableId(type = IdType.INPUT)
private Long id;
/**
* 发货时间
*/
private Date sendTime;
/**
* 收货时间
*/
private LocalDateTime receiveTime;
/**
* 保质期到期时间
*/
private LocalDate dateProduction;
}
写一个接口测试一下
代码语言:javascript复制@PostMapping("testDate")
public AjaxJson testDate(OrderPO orderPO) {
return AjaxJson.success().put("data", orderPO);
}
我们发现成功传输
这里有个弊端,这个@InitBinder
只在当前Controller
生效
我们想要全部Controller
生效则需要写在@RestControllerAdvice
中
例如我们写一个全局日期转换处理器
代码语言:javascript复制package com.ruben.resolver;
import lombok.SneakyThrows;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.swing.text.DateFormatter;
import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Optional;
/**
* 全局日期转换处理器
*
* @author <achao1441470436@gmail.com>
* @date 2021/4/5 0005 0:45
*/
@RestControllerAdvice
public class GlobalTimeResolver {
public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String DATE_PATTERN = "yyyy-MM-dd";
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
public static final ZoneId SYSTEM_DEFAULT_ZONE_ID = ZoneId.systemDefault();
/**
* 我还没有写描述
*
* @param binder 从Web请求参数到JavaBean对象的数据绑定的特殊DataBinder
* @author <achao1441470436@gmail.com>
* @date 2021/4/5 0005 0:48
*/
@InitBinder
protected void initBinder(WebDataBinder binder) {
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
@Override
@SneakyThrows
public void setAsText(String text) {
setValue(textToDate(text));
}
});
// LocalDate类型转换
binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
setValue(textToLocalDate(text));
}
});
// LocalDateTime类型转换
binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
setValue(textToLocalDateTime(text));
}
});
}
/**
* 时间字符串转Date
*
* @param text 2021-04-04 23:10:51
* @return java.util.Date|null
* @author <achao1441470436@gmail.com>
* @date 2021/4/4 0004 23:10
*/
private Date textToDate(String text) {
return Optional.ofNullable(textToLocalDateTime(text)).map(dateTime -> dateTime.atZone(SYSTEM_DEFAULT_ZONE_ID)).map(ZonedDateTime::toInstant).map(Date::from).orElse(null);
}
private LocalDate textToLocalDate(String text) {
return Optional.ofNullable(text).filter(str -> !str.isEmpty()).map(str -> LocalDate.parse(str, DATE_FORMATTER)).orElse(null);
}
/**
* 时间字符串转LocalDateTime
*
* @param text 2021-04-04 23:09:52
* @return java.time.ZonedDateTime|null
* @author <achao1441470436@gmail.com>
* @date 2021/4/4 0004 23:05
*/
private LocalDateTime textToLocalDateTime(String text) {
return Optional.ofNullable(text).filter(str -> !str.isEmpty()).map(str -> LocalDateTime.parse(str, DATE_TIME_FORMATTER)).orElse(null);
}
}
之后我们发现确实所有Controller
都生效了
但有个问题,我们如果是@RequestBody
接参,用的是body
里的json
传输数据
那么这个就不管用了。。。
这个则需要在我们的某个@Configuration
中注入一个自定义的Jackson2ObjectMapperBuilderCustomizer
因为我们mvc
默认使用Jackson
序列化我们的参数
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
JavaTimeModule module = new JavaTimeModule();
module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_TIME_PATTERN)));
module.addSerializer(new LocalDateSerializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_PATTERN)));
module.addSerializer(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_TIME_PATTERN)));
return builder -> {
builder.simpleDateFormat(GlobalTimeResolver.DATE_TIME_PATTERN);
// builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_PATTERN)));
// builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(GlobalTimeResolver.DATE_TIME_PATTERN)));
builder.modules(module);
};
}
我们再写个接口测试下
代码语言:javascript复制@PostMapping("changeOrder")
public AjaxJson changeOrder(@RequestBody OrderPO order) {
mpOrderMapper.updateById(order);
return AjaxJson.success();
}
可以看到成功
注意这里有个坑啊
我们必须至少在JavaTimeModule
指定一个deserializer
再添加进去
否则本配置则失效。。。这个还是从源码看到的
今天踩到这个坑,弄到2021-04-05 01:45:43
有点无语
点进去modules
查看源码
/**
* Specify one or more modules to be registered with the {@link ObjectMapper}.
* Multiple invocations are not additive, the last one defines the modules to
* register.
* <p>Note: If this is set, no finding of modules is going to happen - not by
* Jackson, and not by Spring either (see {@link #findModulesViaServiceLoader}).
* As a consequence, specifying an empty list here will suppress any kind of
* module detection.
* <p>Specify either this or {@link #modulesToInstall}, not both.
* @since 4.1.5
* @see #modules(List)
* @see com.fasterxml.jackson.databind.Module
*/
public Jackson2ObjectMapperBuilder modules(Module... modules) {
return modules(Arrays.asList(modules));
}
翻译过来
注意这句罪魁祸首
坑死人了!!!