Web請求中的日期類型參數

在web請求中,遇到日期類型(Date)一般情況下比較頭疼,因爲可能涉及到各種格式問題,比如“2019-03-07 17:10:21”、“2019-03-07 17:10”、“2019-03-07”,甚至還有1551949821、1551949821000等等各種問題。這些問題,僅僅靠文檔說明,很難保證不出問題。
那怎麼解決這種棘手問題呢?其他各種數據綁定方式就不講了,今天主要講一種結合Springboot特性的無入侵無感知解決方案~
在SpringBoot的WebMvc實現中,專門提供了下面接口用於解決各種格式問題

/**
 * Add {@link Converter Converters} and {@link Formatter Formatters} in addition to the ones
 * registered by default.
 */
default void addFormatters(FormatterRegistry registry) {
}

該方法默認是空的。該方法的作用是從註釋上很容易就明白了,就是註冊Converter和Formatter的,那我們只需要註冊一個日期轉換類型的Converter便可以解決問題了。
那我們先寫一個日期轉換Converter

public class StringDateConverter implements Converter<String, Date> {
    private static Logger logger = LoggerFactory.getLogger(StringDateConverter.class);
    
    private static final String TIME_PATTERN_REGEX = "^\\d{1,13}$";
    
    private static ThreadLocal<SimpleDateFormat[]> dateFormatLocal = new ThreadLocal<SimpleDateFormat[]>() {
        @Override
        protected SimpleDateFormat[] initialValue() {
            return new SimpleDateFormat[] {
                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),
                    new SimpleDateFormat("yyyy-MM-dd HH:mm"),
                    new SimpleDateFormat("yyyy-MM-dd HH"),
                    new SimpleDateFormat("yyyy-MM-dd")
                };
        }
    };
    
    @Override
    public Date convert(final String source) {
        if (source == null || source.trim().equals("")) {
            return null;
        }
        
        Date result = null;
        String _src = source.trim();
        // 1,數字類型
        if (_src.matches(TIME_PATTERN_REGEX)) {
            try {
                long lTime = Long.parseLong(_src);
                if (_src.length() > 10) {
                    result = new Date(lTime);
                } else {
                    result =  new Date(1000L * lTime);
                }
            } catch (Exception e) {
                result = null;
                
                logger.warn("[" + source + "]無法轉化爲日期!");
            }
            
            return result;
        }
        // 2,日期類型
        SimpleDateFormat[] dateFormats = dateFormatLocal.get();
        for (SimpleDateFormat dateFormat : dateFormats) {
            try {
                dateFormat.setLenient(false);
                
                return dateFormat.parse(source);
            } catch (ParseException e) {
                logger.warn("[" + source + "]無法轉化爲" + dateFormat.toPattern() + "格式的日期!");
            }
        }
        
        return null;
    }
}

該StringDateConverter轉換器支持把“yyyy-MM-dd HH:mm:ss”、“yyyy-MM-dd HH:mm”、“yyyy-MM-dd HH”、“yyyy-MM-dd”格式的字符串和long類型的數字轉化爲日期類型,並且是線程安全的。
然後,我們把該轉換器註冊到系統中即可。這也有兩種方案:
方法1,系統中的WebConfig文件繼承WebMvcConfigurer並且重載addFormatters方法:

@Configuration
@Import({ WebMvcAutoConfiguration.class })
@ComponentScan(
        value = "com.beyonds.phoenix.shine.web",
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
        })
public class WebMvcConfiguration implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringDateConverter());
    }
}

方法2,直接把StringDateConverter註冊成bean即可!

@Configuration
@Import({ WebMvcAutoConfiguration.class })
@ComponentScan(
        value = "com.beyonds.phoenix.shine.web",
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
        })
public class WebMvcConfiguration {
    /**
     * 自定義輸入的日期格式
     * 覆蓋spring.mvc.date-format
     * @return 日期格式轉換器
     */
    @Bean
    public StringDateConverter dateConverter() {
        return new StringDateConverter();
    }
}

爲什麼方法2也能生效呢,這就是SpringBoot內部自動機制實現了~
其實,在WebMvcAutoConfiguration內部實現中,有如下代碼

@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);
    }
}

其作用很清楚,就是自動掃描並註冊Converter、GenericConverter和Formatter到web處理器中~
-End-

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章