SpringMvc 请求中日期类型参数接收一二事儿
首先说明:以版本为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 请求中日期类型参数接收一二事儿的更多相关文章
- SpringMVC请求参数接收总结
前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...
- SpringMVC请求参数接收总结(一)
前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...
- 2.5万字长文简单总结SpringMVC请求参数接收
这是公众号<Throwable文摘>发布的第22篇原创文章,暂时收录于专辑<架构与实战>.暂定下一篇发布的长文是<图文分析JUC同步器框架>,下一篇发布的短文是&l ...
- springMvc接受日期类型参数处理
这个问题,也即是springMvc如何进行参数类型的转换 以把client传过来一个String类型,转换为日期类型为例: 1.controller /** * 接收日期类型参数 * 注意: * sp ...
- 【springmvc Request】 springmvc请求接收参数的几种方法
通过@PathVariabl注解获取路径中传递参数 转载请注明出处:springmvc请求接收参数的几种方法 代码下载地址:http://www.zuida@ima@com/share/1751862 ...
- SpringMVC参数绑定(从请求中接受参数)
参数绑定(从请求中接收参数) 1)默认类型: 在controller方法中可以有也可以没有,看自己需求随意添加. httpservletRqeust,httpServletResponse,httpS ...
- SpringMVC表单或Json中日期字符串与JavaBean的Date类型的转换
SpringMVC表单或Json中日期字符串与JavaBean的Date类型的转换 场景一:表单中的日期字符串和JavaBean的Date类型的转换 在使用SpringMVC的时候,经常会遇到表单中的 ...
- 在springmvc框架中,通过ajax请求,响应至前端的中文显示是?
今天遇到的一个问题,我通过ajax请求去访问控制器,然后通过控制器给我响应了一段json数据,但是里面的中文 在浏览上显示是??,我在web.xml 文件中是设置了编码过滤器的,但是估计这个编码过滤器 ...
- SpringMVC请求参数总结
前提 在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结.SpringMVC中处理控制器参数的接口是HandlerMethodArgumentRes ...
随机推荐
- js数据类型以及数组字符串常用方法
JS判断数据类型 例子: var a = "iamstring."; var b = 222; var c= [1,2,3]; var d = new Date(); var e ...
- HBase rebalance 负载均衡源码角度解读使用姿势
关键词:hbase rebalance 负载均衡 参考源码版本:apache-hbase-1.1.2 什么是HBase Rebalance ? 随着数据写入越来越多以及不均衡,即使一开始每个Regio ...
- php基础-cookie&session
设置cookie //设置cookie setcookie('key', 'value', time() + 60, '/'); 设置session //必须开启session session_sta ...
- Android Studio的构建系统:Gradle
原文作者:youxiachai <用Gradle 构建你的android程序> 前言 android gradle 的插件终于把混淆代码的task集成进去了,加上最近,android st ...
- PIL库自我学习总结及应用(美白,磨皮,搞笑图片处理)
Hello!今天我们来学习一下这个神奇的图片处理的第三方函数库——PIL库 (本blog部分图片及代码来自网络) 这是一个支持图像存储.显示和处理的函数库,它能够处理几乎所有图像格式,可以完成对图像的 ...
- html笔记第一天
快速生成标签有序ol>li*3无序ul>(li>a{新闻标题})*3定义列表 dl>(dt+dd)*3制作表格table>(tr>td*5)*6pading:3个数 ...
- Android Studio 去除上方标题
选中代码再Ctrl+shift+'/' 可加/***/注释 https://blog.csdn.net/wuqingsen1/article/details/78554117 styles.xml & ...
- 神经网络_线性神经网络 2 (Nerual Network_Linear Nerual Network 2)
1 LMS 学习规则 1.1 LMS学习规则定义 MSE=(1/Q)*Σe2k=(1/Q)*Σ(tk-ak)2,k=1,2,...,Q 式中:Q是训练样本:t(k)是神经元的期望输出:a(k)是神经元 ...
- 3-2 Hadoop集群伪分布模式配置部署
Hadoop伪分布模式配置部署 一.实验介绍 1.1 实验内容 hadoop配置文件介绍及修改 hdfs格式化 启动hadoop进程,验证安装 1.2 实验知识点 hadoop核心配置文件 文件系统的 ...
- Java提高篇(三):内部类和匿名内部类
一,内部类 其实内部类是十分简单的,我们根据其字里行间的意义就可以知道内部类应该是一个类当中的一个类,相当于一个类进行了嵌套,就如同循环的嵌套一般. 内部类有一个特征:内部类当中可以调用外部类当中的属 ...