springboot接口参数时间转换、解决Date类型参数异常

目录
  1. 1. 默认情况
  2. 2. 局部配置——使用@DateTimeFormat
  3. 3. 全局配置——自定义时间转换器
    1. 3.1. 时间转换类
    2. 3.2. 将时间转换类应用到接口上
      1. 3.2.1. @Component + @PostConstruct
      2. 3.2.2. @ControllerAdvice + @InitBinder

默认情况

在默认情况下,不加任何有关接收Date类型数据的配置时,前端传递Date类型数据至后端接口,控制台出现以下异常:

1
2
Failed to convert from type [java.lang.String] to type [java.util.Date] for value
'2333333333'; nested exception is java.lang.IllegalArgumentException]]

也就是说,在SpringBoot中,必须添加某种配置才能让前端正确传递时间类型数据到后端。下面针对不同的情况来介绍一下解决办法。

局部配置——使用@DateTimeFormat

第一种情况,仅需要对某个Bean类的Date类型字段进行转换,那么只需要在Bean类属性上增加@DateTimeFormat()注解,括号内 pattern为前端传递的日期格式。比如:

1
2
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
Date createTime;

特点–缺陷:

按照如上配置,只能处理形如:2018-11-2 2:22:2这样固定格式的数据,无法处理时间戳和2018-11-2格式的数据。如果想要处理2018-11-2这样的时间,就必须把pattern改成yyyy-MM-dd

可以对不同的属性赋予不同的pattern,但是对每个Date类型都要加上注解显得比较繁琐,也无法处理单个属性可能对应不同格式的值的情况。

更通用、有效的方式是定义一个时间转换类,并将其应用到所有的接口上。

全局配置——自定义时间转换器

分为两个步骤:

  • 编写一个时间转换类,把可能的格式转换为Date类型
  • 通过某种方式,将时间转换应用到Spring的所有接口上

时间转换类

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
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* 日期转换类
* 将标准日期、标准日期时间、时间戳转换成Date类型
*/
public class DateConverter implements Converter<String, Date> {
private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
private static final String shortDateFormat = "yyyy-MM-dd";
private static final String timeStampFormat = "^\\d+$";

@Override
public Date convert(String value) {

if(StringUtils.isEmpty(value)) {
return null;
}

value = value.trim();

try {
if (value.contains("-")) {
SimpleDateFormat formatter;
if (value.contains(":")) {
formatter = new SimpleDateFormat(dateFormat);
} else {
formatter = new SimpleDateFormat(shortDateFormat);
}
return formatter.parse(value);
} else if (value.matches(timeStampFormat)) {
Long lDate = new Long(value);
return new Date(lDate);
}
} catch (Exception e) {
throw new RuntimeException(String.format("parser %s to Date fail", value));
}
throw new RuntimeException(String.format("parser %s to Date fail", value));
}
}

将时间转换类应用到接口上

介绍两种方式:使用@Component + @PostConstruct@ControllerAdvice + @InitBinder

第一种方式:

@Component + @PostConstruct

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
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import javax.annotation.PostConstruct;

/**
* @author zfh
* @version 1.0
* @date 2018/12/30 10:16
*/
@Component
public class WebConfigBeans {

@Autowired
private RequestMappingHandlerAdapter handlerAdapter;

@PostConstruct
public void initEditableAvlidation() {

ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer)handlerAdapter.getWebBindingInitializer();
if(initializer.getConversionService()!=null) {
GenericConversionService genericConversionService = (GenericConversionService)initializer.getConversionService();

genericConversionService.addConverter(new DateConverterConfig());

}
}
}

第二种方式:

@ControllerAdvice + @InitBinder

有关这两个注解的含义请参考我的博客:Spring进阶之@ControllerAdvice与统一异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import com.aegis.config.converter.DateConverter;
import com.aegis.model.bean.common.JsonResult;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

/**
* @author zfh
* @version 1.0
* @since 2019/1/4 15:23
*/
@ControllerAdvice
public class ControllerHandler {

@InitBinder
public void initBinder(WebDataBinder binder) {
GenericConversionService genericConversionService = (GenericConversionService) binder.getConversionService();
if (genericConversionService != null) {
genericConversionService.addConverter(new DateConverter());
}
}
}

OK,关于SpringBoot处理前端日期传值的问题的相关解决办法就介绍到这里了。