首先说明:以版本为Spring 4.3.0为测试对象; 开启<mvc:annotation-driven />


测试场景一:请求中含有date属性,该类型为日期类型,SpringMvc采用@RequestParam来接受作为方法入参。

代码很简单,第一反应是不能将字符串的date属性赋给d;

先尝试输入当前日期 2019-02-21 20:30 并提交,当然现在大多都是前端日期控件来选择日期并按照一定类型提交到后台的;

    @RequestMapping(value="/form9")
@ResponseBody
public String form9(@RequestParam(name="date") Date d) {
//基本类型会转成包装类型,尝试转换
return "form9 Response Ok! " + d;
}

查看报错信息: 没能够将字符串类型转换需要的日期类型

Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam java.util.Date] for value '2019-02-21 20:30'; nested exception is java.lang.IllegalArgumentException
at org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:109)
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:36)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:173)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:108)
at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:64)
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:47)
at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:688)

其实,不是这样的,当输入日期为   2018/02/21 20:36:00 这样的,你会发现又可以将字符串转为日期类型

原因我DEBUG简单分析如下:因为@RequestParam注解决定了使用 RequestParamMethodArgumentResolver这个参数解析器,mvc-annotation注册的参数类型转换器 并没有

String—>Date类型的转换器 , 但是用到了ObjectToObjectConvert这个转换器;下图贴一下其 类型转换的convert 方法,就是尝试去寻找 目标类 Date 构造方法

即目标类构造方法需要唯一含有String类型的构造方法,然后实例化 该目标类,  没找到就会抛出异常;

public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Class<?> sourceClass = sourceType.getType();
Class<?> targetClass = targetType.getType();
Member member = getValidatedMember(targetClass, sourceClass); try {
if (member instanceof Method) {
Method method = (Method) member;
ReflectionUtils.makeAccessible(method);
if (!Modifier.isStatic(method.getModifiers())) {
return method.invoke(source);
}
else {
return method.invoke(null, source);
}
}
else if (member instanceof Constructor) {
Constructor<?> ctor = (Constructor<?>) member;
return ctor.newInstance(source);
}
}
catch (InvocationTargetException ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex.getTargetException());
}
catch (Throwable ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
} // If sourceClass is Number and targetClass is Integer, the following message should expand to:
// No toInteger() method exists on java.lang.Number, and no static valueOf/of/from(java.lang.Number)
// method or Integer(java.lang.Number) constructor exists on java.lang.Integer.
throw new IllegalStateException(String.format("No to%3$s() method exists on %1$s, " +
"and no static valueOf/of/from(%1$s) method or %3$s(%1$s) constructor exists on %2$s.",
sourceClass.getName(), targetClass.getName(), targetClass.getSimpleName()));
}

因为Date有个虽然过时、但是确实是String类的构造方法:至于parse方法就是将String字符串转为long类型,支持格式有常见的几种:

2018/02/21 20:36:00  或者  2019/02/21 或者  02/21/2019 20:58:00

具体看是否支持你传过来的日期格式,new Date(“your pattern”)是否抛出异常即可

    @Deprecated
public Date(String s) {
this(parse(s));
}

为了验证我的说法,改动下一些地方:接收参数改成自定义MyDate对象,其中name属性只是为了指定将名字为 date 的参数传递给MyDate的惟一的String构造方法;

@RequestMapping(value="/form9")
@ResponseBody
public String form9(@RequestParam(name="date") MyDate d) {
//基本类型会转成包装类型,尝试转换
return "form9 Response Ok! " + d;
} public class MyDate {
public MyDate(String str) throws ParseException {
System.out.println("调用MyDate构造器");
this.date = new SimpleDateFormat("yyyy-MM-dd").parse(str);
}
private Date date; @Override
public String toString() {
return "MyDate{" +"date=" + date +'}';
}
}

测试结果呢? 证明  SpringMvc mvc-annotation开启,也是能够将字符串类型用Date类型接收,只是接收方式就是调用Date的一个参数String类型的构造器转换成Date类型;


测试场景二.@DateTimeFormat 接收自定义日期格式

说明:使用@DateTimeFormat,在<mvc:annotation-driven />基础上,Spring版本4.3.0

有两种方式:方式一,只需要当前项目编译为JDK1.7、1.8以及更高版本,会自动支持@DateTimeFormat注解;

方式二,低版本的话需要引入 joda-time jar包;(看到网上有种说法需要引入该包才能支持@DateTimeFormat,觉得片面了,JDK1.7以及以上可以不添加该jar包);

至于@DateTimeFormat用法有两种:

方式一:  结合@RequestParam

@RequestMapping(value="/form9")
@ResponseBody
public String form9(@RequestParam(name="date") @DateTimeFormat(pattern = "MM-yyyy-dd") Date d) {
return "form9 Response Ok! " + d;
}

方式二: SpringMvc接收参数为自定义Java对象,在自定义的Java对象属性上标注@DateTimeFormat注解;

(重要的是这种情况下Java对象必须要有空参的构造器,以及对应日期属性的set方法,没有构造器就抛出异常无法实例化Java对象,没有set方法就是Java对象属性为null)

@RequestMapping(value="/form8")
@ResponseBody
public String form8(TimePojo pojo) {
return "form8 Response Ok! " + pojo;
} @Setter
@Getter
@ToString
//节约篇幅,setter、getter、ToString来自lombok
public class TimePojo {
@DateTimeFormat(pattern = "yyyy-mm-dd")
private Date date;
}

下面花一些篇幅记录下,我Debug过程中分析的两种方式的异同:

方式一而言,参数解析器之前提过,RequestParamMethodArgumentResolver这个解析器来解析@RequestParam参数这点是不变的, 转换器之前迫不得已找的ObjectToObjectConverter,

这次使用到的Converter,是AnnotationParserConverter,其专门针对String—>@DateTimeFormat的转换器,  这些转换器都注册在SpringMvc的ConversionService中;

看下AnnotationParserConverter的convert方法,最后几行调用了ParserConvert的convert方法,ParserConvert继承自GenericConvert,其convert方法就是使用它的

parser对象的parse方法,当前情况也就是DateFormatter的parser方法, 下图可以看到其parse方法等同于new SimpleDateFormat(“patten”).parse(“..”);

public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
  //当前情况是  获取@DateTimeFormat注解
Annotation ann = targetType.getAnnotation(this.annotationType);
if (ann == null) {
throw new IllegalStateException(
"Expected [" + this.annotationType.getName() + "] to be present on " + targetType);
}
AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, targetType.getObjectType());
GenericConverter converter = cachedParsers.get(converterKey);
if (converter == null) {
Parser<?> parser = this.annotationFormatterFactory.getParser(
converterKey.getAnnotation(), converterKey.getFieldType());
                //当前情况annotationFormatterFactory为DateTimeFormatAnnotationFormatterFactory
  //获取到的parser对象是 DateFormatter
                      converter = new ParserConverter(this.fieldType, parser, FormattingConversionService.this);
cachedParsers.put(converterKey, converter);
}
return converter.convert(source, sourceType, targetType);
}

到这里也就完成了请求request中的String类型参数赋给方法Date类型入参,你以为到这里就结束了,看下面两种情况也是同样可以接受并转换参数的!

补充说明:之前DateFormatter返回了Date类型参数,下图是ParserConvert的convert方法,注意到paser完成以后, Date不符合需要参数Calendar类型要求,于是继续调用conversionService的convert进行解析,正好SpringMvc <mvc:annotation-driven/>替我们注册了Date->Calendar的转换器,转换的方法也非常简单,

Calendar calendar = Calendar.getInstance();calendar.setTime(source); Long类型的转换器也是类似的因为SpringMvc注册的Date->Long的转换器, date.getTime()即完成转换;

方式二而言,参数解析器是ServletModelAttributeMethodProcessor,作用就是将请求参数映射到Java对象的属性上;重要的一点,该Java对象需要有空参public类型构造器,不然无法实例化抛出异常,这种方式也支持级联属性设置,同样的级联的属性也需要有空参构造方法; 看到过网上有个笔记 lombok的@Builder注解会使这种方式接受请求参数映射到属性失效,因为生成的class文件破坏了默认构造器,就是没有空的构造方法了;

扯得有点远了,关于日期类型总结下几点:

不管SpringMvc是否默认支持将请求中字符串转为Date类型,最轻松的方式应该就是使用@DateFormat注解,1.7以及更高的1.8版本直接就可以使用,其他还有方式比如自定义conversion-service

SpringMvc 请求中日期类型参数接收一二事儿的更多相关文章

  1. SpringMVC请求参数接收总结

    前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...

  2. SpringMVC请求参数接收总结(一)

    前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...

  3. 2.5万字长文简单总结SpringMVC请求参数接收

    这是公众号<Throwable文摘>发布的第22篇原创文章,暂时收录于专辑<架构与实战>.暂定下一篇发布的长文是<图文分析JUC同步器框架>,下一篇发布的短文是&l ...

  4. springMvc接受日期类型参数处理

    这个问题,也即是springMvc如何进行参数类型的转换 以把client传过来一个String类型,转换为日期类型为例: 1.controller /** * 接收日期类型参数 * 注意: * sp ...

  5. 【springmvc Request】 springmvc请求接收参数的几种方法

    通过@PathVariabl注解获取路径中传递参数 转载请注明出处:springmvc请求接收参数的几种方法 代码下载地址:http://www.zuida@ima@com/share/1751862 ...

  6. SpringMVC参数绑定(从请求中接受参数)

    参数绑定(从请求中接收参数) 1)默认类型: 在controller方法中可以有也可以没有,看自己需求随意添加. httpservletRqeust,httpServletResponse,httpS ...

  7. SpringMVC表单或Json中日期字符串与JavaBean的Date类型的转换

    SpringMVC表单或Json中日期字符串与JavaBean的Date类型的转换 场景一:表单中的日期字符串和JavaBean的Date类型的转换 在使用SpringMVC的时候,经常会遇到表单中的 ...

  8. 在springmvc框架中,通过ajax请求,响应至前端的中文显示是?

    今天遇到的一个问题,我通过ajax请求去访问控制器,然后通过控制器给我响应了一段json数据,但是里面的中文 在浏览上显示是??,我在web.xml 文件中是设置了编码过滤器的,但是估计这个编码过滤器 ...

  9. SpringMVC请求参数总结

    前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...

随机推荐

  1. Android 性能优化之使用Lint

    代码静态检查推荐: (1)Android自定义Lint实践 (2)美团外卖Android Lint代码检查实践 (3)Android自定义Lint实践2——改进原生Detector Lint 是 An ...

  2. 资源验证(Modified)

    Last-Modified : 上次修改时间 配合 If-Modified-Since 或者 If-Unmodified-Since  (请求头携带) 对比上次修改时间对资源进行验证 Etag验证 数 ...

  3. KMP性质小结

    跪拜_Blackjack_神犇

  4. npm几个常用命令

    npm install xxx 安装模块(例如npm install express 就会默认安装express的最新版本,也可以通过在后面加版本号的方式安装指定版本,如npm install exp ...

  5. 如何判断dt中所有行的状态并有选择的移除

    DataRow drFocusedRow = dtCentralizerOptimalSelection.Rows[gvCentralizerOptimalSelection.FocusedRowHa ...

  6. verilog中timescale

    1. `timescale `timescale是Verilog中的预编译指令,指定位于它后边的module的时间单位和时间精度,直到遇到新的`timescale指令或者`resetall指令.它的语 ...

  7. QEMU KVM Libvirt手册(9): network

    虚拟网卡由-net nic定义 # qemu-system-x86_64 -enable-kvm -name ubuntutest  -m 2048 -hda ubuntutest.img -vnc ...

  8. 【安富莱二代示波器教程】第17章 附件B---功能扩展和改进方向

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=45785 第17章      附件B---功能扩展和改进方向   ...

  9. 吴恩达机器学习笔记52-异常检测的问题动机与高斯分布(Problem Motivation of Anomaly Detection& Gaussian Distribution)

    一.问题动机 异常检测(Anomaly detection)问题是机器学习算法的一个常见应用.这种算法的一个有趣之处在于:它虽然主要用于非监督学习问题,但从某些角度看,它又类似于一些监督学习问题. 给 ...

  10. [Java]LeetCode116. 填充同一层的兄弟节点 | Populating Next Right Pointers in Each Node

    Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...