上一篇中使用的是全注解下的时间参数处理,缺点较为明显:需要频繁加入注解。本文提供第二种处理方式:使用全局字符串方式处理入参时间,如入参:2022-07-06 12:01:01,这种方式是最好的传参方式。
- 优点:参数上不需要加任何注解,即可全局统一入参格式&出参格式,且报文可阅读
- 缺点:要求入参出参时间格式统一
传送链接:
- 日期时间对象全注解方式出入参:https://www.jianshu.com/p/7049d5161c16
- 日期时间对象全局时间戳出入参:https://www.jianshu.com/p/bf9ad578bebc
代码实现:
1.定义相关常量:TimeConstants
/**
* 时间转换常量
*
* @author wgb
* @date 2021/1/15 15:00
*/
public interface TimeConstants {
/**
* 默认日期时间格式正则
*/
String DEFAULT_DATE_TIME_REGULAR = "^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$";
/**
* 默认日期格式正则
*/
String DEFAULT_DATE_REGULAR = "^\\d{4}-\\d{1,2}-\\d{1,2}$";
/**
* 默认时间格式正则
*/
String DEFAULT_TIME_REGULAR = "^\\d{1,2}:\\d{1,2}:\\d{1,2}$";
/**
* 默认日期时间格式
*/
String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* 默认日期格式
*/
String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
/**
* 默认时间格式
*/
String DEFAULT_TIME_FORMAT = "HH:mm:ss";
}
2.转换类核心代码:StringToDateConverter
import com.vip.tools.format.string.constant.TimeConstants;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义类型转换, @RequestParam/@PathVariable的日期字符串转换对应日期类型
*
* @author wgb
* @date 2021/01/14 15:57
*/
@Configuration
public class StringToDateConverter<T> {
private static final Map<String, Class> CLAZZ_MAP = new HashMap<>();
static {
CLAZZ_MAP.put("HH:mm:ss", LocalTime.class);
CLAZZ_MAP.put("yyyy-MM-dd", LocalDate.class);
CLAZZ_MAP.put("yyyy-MM-dd HH:mm:ss", LocalDateTime.class);
}
@Bean(name = "localDateTimeConverter")
public Converter<String, LocalDateTime> localDateTimeConverter() {
return new Converter<String, LocalDateTime>() {
@Override
public LocalDateTime convert(@Nullable String source) {
return (LocalDateTime) parse(source);
}
};
}
@Bean(name = "localDateConverter")
public Converter<String, LocalDate> localDateConverter() {
return new Converter<String, LocalDate>() {
@Override
public LocalDate convert(@Nullable String source) {
return (LocalDate) parse(source);
}
};
}
@Bean(name = "localTimeConverter")
public Converter<String, LocalTime> localTimeConverter() {
return new Converter<String, LocalTime>() {
@Override
public LocalTime convert(@Nullable String source) {
return (LocalTime) parse(source);
}
};
}
@Bean(name = "dateConverter")
public Converter<String, Date> dateConverter() {
return new Converter<String, Date>() {
@Override
@SneakyThrows
public Date convert(@Nullable String source) {
if (StringUtils.isBlank(source)) {
return null;
}
if (source.matches(TimeConstants.DEFAULT_DATE_TIME_REGULAR)) {
return new SimpleDateFormat(TimeConstants.DEFAULT_DATE_TIME_FORMAT).parse(source);
}
throw new IllegalArgumentException("Invalid Time Value of Type String:" + source);
}
};
}
/**
* 根据字符串进行解析,将Date转LocalDateTime
*/
@SneakyThrows
@SuppressWarnings(value = "unchecked")
private T parse(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
source = source.trim();
if (source.matches(TimeConstants.DEFAULT_DATE_TIME_REGULAR)) {
//判断是否yyyy-MM-dd HH:mm:ss格式
return (T) LocalDateTime.parse(source, DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_DATE_TIME_FORMAT));
} else if (source.matches(TimeConstants.DEFAULT_DATE_REGULAR)) {
//判断是否yyyy-MM-dd格式
return (T) LocalDate.parse(source, DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_DATE_FORMAT));
} else if (source.matches(TimeConstants.DEFAULT_TIME_REGULAR)) {
//判断是否HH:mm:ss格式
return (T) LocalTime.parse(source, DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_TIME_FORMAT));
} else {
throw new IllegalArgumentException("Invalid Time Value of Type String:" + source);
}
}
}
3.Jackson参数转换器配置
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.vip.tools.format.string.constant.TimeConstants;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* 使用官方自带的json格式类库
* 可以处理多种格式化方式,这里只使用了时间格式化
*
* @author wgb
* @date 2020/11/25 18:07
*/
public class JacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {
public JacksonHttpMessageConverter() {
handleDateTime();
}
/**
* 使用ObjectMapper处理@RequestBody数据
*/
private void handleDateTime() {
ObjectMapper objectMapper = this.getObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// Date出入参格式化
objectMapper.setDateFormat(new SimpleDateFormat(TimeConstants.DEFAULT_DATE_TIME_FORMAT));
// Java8时间格式化
JavaTimeModule javaTimeModule = new JavaTimeModule();
// 出参格式化
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_DATE_TIME_FORMAT)));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_DATE_FORMAT)));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_TIME_FORMAT)));
// 入参格式化
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_DATE_TIME_FORMAT)));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_DATE_FORMAT)));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_TIME_FORMAT)));
// 把“忽略重复的模块注册”禁用,否则下面的注册(LocalDateTime)不生效
objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
objectMapper.registerModule(javaTimeModule);
// 然后再设置为生效,避免被其他地方覆盖
objectMapper.enable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
// 设置格式化内容
this.setObjectMapper(objectMapper);
}
}
4.WebMvcConfiguration
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;
/**
* WebMvcConfiguration
*
* @author wgb
* @date 2020/11/25 16:45
*/
@Configuration
@RequiredArgsConstructor
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
private final Converter<String, LocalDateTime> localDateTimeConverter;
private final Converter<String, LocalDate> localDateConverter;
private final Converter<String, LocalTime> localTimeConverter;
private final Converter<String, Date> dateConverter;
/**
* 入参时间格式化
*/
@Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(localDateTimeConverter);
registry.addConverter(localDateConverter);
registry.addConverter(localTimeConverter);
registry.addConverter(dateConverter);
}
/**
* extends WebMvcConfigurationSupport
* 以下 spring-boot: jackson时间格式化 配置 将会失效
* spring.jackson.time-zone=GMT+8
* spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
* 原因: 会覆盖 @EnableAutoConfiguration 关于 WebMvcAutoConfiguration 的配置
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
JacksonHttpMessageConverter converter = new JacksonHttpMessageConverter();
converters.add(converter);
// 添加二进制数组转换器:用以文件下载时二进制流的响应
converters.add(new ByteArrayHttpMessageConverter());
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.maxAge(3600)
.allowCredentials(true);
}
}
开始写测试代码
DTO
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
/**
* description
*
* @author wgb
* @date 2021/1/14 15:57
*/
@Data
public class CityDTO {
/**
* date
*/
private Date date;
/**
* localDate
*/
private LocalDate localDate;
/**
* localDateTime
*/
private LocalDateTime localDateTime;
/**
* localTime
*/
private LocalTime localTime;
/**
* 附加属性
*/
private String name;
}
controller
import com.vip.tools.format.string.dto.CityDTO;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
/**
* 时间格式化 控制器
*
* @author wgb
* @date 2021/1/15 13:25
*/
@RestController
@RequestMapping(value = "/string_format")
public class StringDateFormatController {
/**
* 格式化测试
*
* @return
*/
@PostMapping("/test")
public CityDTO format(@RequestParam Date date, @RequestParam LocalDate localDate, @RequestParam LocalDateTime localDateTime
, @RequestParam LocalTime localTime, @RequestBody CityDTO dto) {
System.out.println(date);
System.out.println(localDate);
System.out.println(localDateTime);
System.out.println(localTime);
System.out.println(dto);
return dto;
}
}
URL:http://localhost:9600/string_format/test?date=2020-01-25 12:00:23&localDate=2020-01-25&localDateTime=2020-01-25 12:00:23&localTime=12:00:23
Body参数:
{
"date": "2020-12-31 23:59:59",
"localDate": "2020-12-31",
"localDateTime": "2020-12-31 23:59:59",
"localTime": "23:59:59",
"name": "TEST"
}
控制台打印:
Sat Jan 25 12:00:23 CST 2020
2020-01-25
2020-01-25T12:00:23
12:00:23
CityDTO(date=Thu Dec 31 23:59:59 CST 2020, localDate=2020-12-31, localDateTime=2020-12-31T23:59:59, localTime=23:59:59, name=TEST)
响应结果:
{
"date": "2020-12-31 23:59:59",
"localDate": "2020-12-31",
"localDateTime": "2020-12-31 23:59:59",
"localTime": "23:59:59",
"name": "TEST"
}
日期对象全局字符串出入参配置完成。