This situation should be treated differently from when time is used as a Json string, because the bottom layer of the front-end json to back-end pojo uses the Json serialization Jackson tool (HttpMessgeConverter
); when the time string is passed in as a normal request parameter, Converter
is used for conversion. There is a difference in the processing methods between the two.
Implementationorg.springframework.core.convert.converter.Converter
, custom parameter converter, as follows:
@Configuration public class DateConverterConfig { @Bean public Converter<String, LocalDate> localDateConverter() { return new Converter<String, LocalDate>() { @Override public LocalDate convert(String source) { return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd")); } }; } @Bean public Converter<String, LocalDateTime> localDateTimeConverter() { return new Converter<String, LocalDateTime>() { @Override public LocalDateTime convert(String source) { return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } }; } }
Comment: The above two beans will be injected into the parameter parser of spring mvc (it seems to be called ParameterConversionService
). When the incoming string is to be converted to the LocalDateTime class, spring will call the Converter Convert this input parameter.
Note: Regarding the custom parameter converter Converter, there is a pitfall. If the above anonymous inner class is simplified into a lambda expression:
@Bean @ConditionalOnBean(name = "requestMappingHandlerAdapter") public Converter<String, LocalDate> localDateConverter() { return source -> LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)); }
When the project is started again An exception will occur:
Caused by: java.lang.IllegalArgumentException: Unable to determine source type 1eefd63bbe027a2807ccada294a3372c and target type 8742468051c85b06f0a0af9e3e506b5c for your Converter [com.example.demo126.config. MappingConverterAdapter$$Lambda$522/817994751]; does the class parameterize those types?
Because:
It will be initialized when the web project starts the registrationrequestMappingHandlerAdapter
##WebBindingInitializer
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());And
ConfigurableWebBindingInitializer requires
FormattingConversionService, and
FormattingConversionService will add all
Converter Come in, you need to obtain generic information when adding:
@Override public void addFormatters(FormatterRegistry registry) { for (Converter<?, ?> converter : getBeansOfType(Converter.class)) { registry.addConverter(converter); } for (GenericConverter converter : getBeansOfType(GenericConverter.class)) { registry.addConverter(converter); } for (Formatter<?> formatter : getBeansOfType(Formatter.class)) { registry.addFormatter(formatter); } }Adding Converter.class usually obtains the specific types of two generics through the interface
public ResolvableType as(Class<?> type) { if (this == NONE) { return NONE; } Class<?> resolved = resolve(); if (resolved == null || resolved == type) { return this; } for (ResolvableType interfaceType : getInterfaces()) { ResolvableType interfaceAsType = interfaceType.as(type); if (interfaceAsType != NONE) { return interfaceAsType; } } return getSuperType().as(type); }The interface of Lambda expression is Converter, and The specific type cannot be obtained. In this case, the solution is:
requestMappingHandlerAdapterbean is registered and then add your own converter and it will not be registered in
FormattingConversionService
@Bean @ConditionalOnBean(name = "requestMappingHandlerAdapter") public Converter<String, LocalDateTime> localDateTimeConverter() { return source -> LocalDateTime.parse(source, DateTimeUtils.DEFAULT_FORMATTER); }can also perform regular matching on the string passed by the front end, such as yyyy-MM-dd HH:mm:ss, yyyy-MM-dd, HH:mm:ss, etc., for matching. to adapt to various scenarios.
@Component public class DateConverter implements Converter<String, Date> { @Override public Date convert(String value) { /** * 可对value进行正则匹配,支持日期、时间等多种类型转换 * 这里在匹配Date日期格式时直接使用了 hutool 为我们已经写好的解析工具类,这里就不重复造轮子了 * cn.hutool.core.date.DateUtil * @param value * @return */ return DateUtil.parse(value.trim()); } }Note: Here, hutool is used directly when matching the Date date format. It is the parsing tool class we have already written. We will not reinvent the wheel here. The following method also uses this tool class. If you want It is also very simple to use this tool class in your own project. Just introduce the hutool dependency in the project pom file, as follows:
<!--hu tool 工具类--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.1.3</version> </dependency>Use Spring annotationsUse spring's own annotations
@DateTimeFormat(pattern = "yyyy-MM-dd"), as follows:
@DateTimeFormat(pattern = "yyyy-MM-dd") private Date startDate;If a custom parameter converter is used, Spring will preferentially use this method for processing, that is, Spring annotations will not Take effect. Use ControllerAdvice with initBinder
@ControllerAdvice public class GlobalExceptionHandler { @InitBinder protected void initBinder(WebDataBinder binder) { binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() { @Override public void setAsText(String text) throws IllegalArgumentException { setValue(LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd"))); } }); binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() { @Override public void setAsText(String text) throws IllegalArgumentException { setValue(LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } }); binder.registerCustomEditor(LocalTime.class, new PropertyEditorSupport() { @Override public void setAsText(String text) throws IllegalArgumentException { setValue(LocalTime.parse(text, DateTimeFormatter.ofPattern("HH:mm:ss"))); } }); } }
post,content-type=application/json, and the background uses
@RequestBody Reception, the default reception and return value format is:
yyyy-MM-dd HH:mm:ss
spring:Supportjackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT 8
(content-type=application/json) The string format in the request is yyyy-MM-dd
HH:mm:ss, use
@ in the background RequestBody is received, and the return value date is converted to
yyyy-MM-dd HH:mm:ss format string;
content-type=application/json ) The strings of
yyyy-MM-dd and other types in the request are converted to
date;
java8date
api is not supported;
@Configuration public class JacksonConfig { /** 默认日期时间格式 */ 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"; @Bean public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); ObjectMapper objectMapper = new ObjectMapper(); // 忽略json字符串中不识别的属性 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略无法转换的对象 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // PrettyPrinter 格式化输出 objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true); // NULL不参与序列化 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 指定时区 objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8:00")); // 日期类型字符串处理 objectMapper.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT)); // java8日期日期处理 JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))); javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))); javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))); objectMapper.registerModule(javaTimeModule); converter.setObjectMapper(objectMapper); return converter; } }
The above is the detailed content of How to handle Spring Boot date and time. For more information, please follow other related articles on the PHP Chinese website!