springboot Date类型入参转换问题

最近工作中遇到date类型转换出错的情况,然后就对问题进行了一次深入挖掘,并整理出来给大家分享。

如果只需要解决问题,请直接拖动到底部

一 问题描述:

 date类型参数一般传入 yyyy-MM-dd 或者 毫秒数 这两种,上面这两种适用于post请求入参,在get请求下只会支持yyyy/MM/dd这种形势,下面是两种请求方式支持的date传参类型:

请求方式\入参格式

2020/01/01

2020-01-01

1584499678000

Post

×

Get

×

×

报错信息:

Post :2020/01/01

springboot Date类型入参转换问题

Get:2020-01-01

springboot Date类型入参转换问题

 

二 查看具体问题:

首先进入DispatcherServlet中,所有的请求都会由它来进行分发,找到doDispatch方法,分发发生在这个方法中:

先判断是否是文件上传,获取request对应的Handler方法,通过Handler获取HandlerAdpater

springboot Date类型入参转换问题

然后调用相应的handle方法去处理request,首先会去检查request请求,然后判断是否需要加锁,接着去调用invokeHandlerMethod

springboot Date类型入参转换问题

invokeAndHandler内部调用的是invokeForReuquest,根据当前的请求获取Method对应的参数,然后进行invoke.

springboot Date类型入参转换问题

springboot Date类型入参转换问题

springboot Date类型入参转换问题

这个方法是解析Method参数的,入参转换出错发生在这一步

springboot Date类型入参转换问题

springboot Date类型入参转换问题

springboot Date类型入参转换问题

springboot Date类型入参转换问题

conversionService支持128种类型转换

springboot Date类型入参转换问题

springboot Date类型入参转换问题

这里根据source和target 的类型来获取对应的method或者constructor 的反射信息,然后授权进行调用,并返回转换类型后的参数

springboot Date类型入参转换问题

入参类型为java.lang.String,目标类型为java.util.Date,组装出来得Menber应该 public java.util.Date(java.lang.String),进入Date类中,找到该Method,发现parse方法不支持'yyyy-MM-dd'这种格式,支持'yyyy/MM/dd'这种格式。如果get输入的是毫秒数也会进入这个方法去做类型转换,因为所有入参会自动识别为string类型。

当post 入参为一个vo对象时,猜测应该时通过json来做转换的,因为没看懂如何转换成一个vo对象的,只能通过报错信息来猜:

JSON parse error: Cannot deserialize value of type `java.util.Date` from String "2020/01/01": not a valid representation (error: Failed to parse Date value '2020/01/01': Cannot parse date "2020/01/01": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))

三 解决方法

1.手动添加一个converter,后面如果在需要string->date 转换就会先来调用这个converter,因为它被放到对应list的头部,所以会优先调用它

@Componet
public class DateConverterConfig implements Converter<String, Date> {
  private static final List<String> formarts = new ArrayList<>(4);
  static {
    formarts.add("yyyy-MM");
    formarts.add("yyyy-MM-dd");
    formarts.add("yyyy-MM-dd hh:mm");
    formarts.add("yyyy-MM-dd hh:mm:ss");
  }

  @Override
  public Date convert(String source) {
    String value = source.trim();
    if ("".equals(value)) {
      return null;
    }
    if (source.matches("^\\d{4}-\\d{1,2}$")) {
      return parseDate(source, formarts.get(0));
    } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
      return parseDate(source, formarts.get(1));
    } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
      return parseDate(source, formarts.get(2));
    } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
      return parseDate(source, formarts.get(3));
    } else {
      throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
    }
  }
  
  public Date parseDate(String dateStr, String format) {
    Date date = null;
    try {
      DateFormat dateFormat = new SimpleDateFormat(format);
      date = dateFormat.parse(dateStr);
    } catch (Exception e) {

    }
    return date;
  }

}

2.在对应controller里加一个属性编辑器,作用是先将date类型数据格式化,再做绑定操作,同样可以避免调用date中的方法报错

@InitBinder
protected void initBinder(WebDataBinder binder) {
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}

参考链接:https://blog.csdn.net/eumenides_/article/details/79033505