大部分人在二三十岁上就死去了,因为过了这个年龄,他们只是自己的影子,此后的余生则是在模仿自己中度过,日复一日,更机械,更装腔作势地重复他们在有生之年的所作所为,所思所想,所爱所恨。——《约翰 • 克利斯朵夫》
我们在开发中对于日期,如果是协商好,请求发送指定格式的日期字符串
我们这边再转换成对应日期格式,如果每个都一个一个来转,非常麻烦,不方便
这里介绍一种全局的mvc
转换方式
使用@InitBinder
注解以及Jackson2ObjectMapperBuilderCustomizer
如果我们使用application/x-www-form-urlencoded
接参,则@InitBinder
上场了
只需要在Controller
中写下如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 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();@InitBinder protected void initBinder (WebDataBinder binder) { binder.registerCustomEditor(Date.class, new PropertyEditorSupport () { @Override @SneakyThrows public void setAsText (String text) { setValue(textToDate(text)); } }); binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport () { @Override public void setAsText (String text) { setValue(textToLocalDate(text)); } }); binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport () { @Override public void setAsText (String text) { setValue(textToLocalDateTime(text)); } }); } 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 ); } 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @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; }
写一个接口测试一下
1 2 3 4 @PostMapping("testDate") public AjaxJson testDate (OrderPO orderPO) { return AjaxJson.success().put("data" , orderPO); }
我们发现成功传输
这里有个弊端,这个@InitBinder
只在当前Controller
生效
我们想要全部Controller
生效则需要写在@RestControllerAdvice
中
例如我们写一个全局日期转换处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 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;@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(); @InitBinder protected void initBinder (WebDataBinder binder) { binder.registerCustomEditor(Date.class, new PropertyEditorSupport () { @Override @SneakyThrows public void setAsText (String text) { setValue(textToDate(text)); } }); binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport () { @Override public void setAsText (String text) { setValue(textToLocalDate(text)); } }); binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport () { @Override public void setAsText (String text) { setValue(textToLocalDateTime(text)); } }); } 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 ); } 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
序列化我们的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 @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.modules(module ); }; }
我们再写个接口测试下
1 2 3 4 5 @PostMapping("changeOrder") public AjaxJson changeOrder (@RequestBody OrderPO order) { mpOrderMapper.updateById(order); return AjaxJson.success(); }
可以看到成功
注意这里有个坑啊
我们必须至少在JavaTimeModule
指定一个deserializer
再添加进去
否则本配置则失效。。。这个还是从源码看到的
今天踩到这个坑,弄到2021-04-05 01:45:43
有点无语
点进去modules
查看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public Jackson2ObjectMapperBuilder modules (Module... modules) { return modules(Arrays.asList(modules)); }
翻译过来
注意这句罪魁祸首
坑死人了!!!