时间出入参格式化-2:全局String出入参(推荐)

上一篇中使用的是全注解下的时间参数处理,缺点较为明显:需要频繁加入注解。本文提供第二种处理方式:使用全局字符串方式处理入参时间,如入参:2022-07-06 12:01:01,这种方式是最好的传参方式。

  • 优点:参数上不需要加任何注解,即可全局统一入参格式&出参格式,且报文可阅读
  • 缺点:要求入参出参时间格式统一
传送链接:

代码实现:

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"
}

日期对象全局字符串出入参配置完成。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。