首先说明:以版本为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. 在centos 7云服务器上搭建Apache服务器并访问到你的网站

    网站是指在互联网上根据一定的规则,用HTML等语言制作的网页的集合.网站的目的是用来展示一些信息,如果是个人网站则是为了展示自己的一些想被人知道的东西,例如自己的一些作品,又或者是通过网站来达到盈利的 ...

  2. 使用mongodb的一些笔记

    show dbs # 从结果中发现有cmb_demo_23_hackeruse cmb_demo_23_hacker db.all_in_one.find({"_id":15480 ...

  3. 2.postman安装及使用

    一.postman说明 postman是研发和测试进行接口调试的工具.可以用来很方便的模拟get或者post或者其他方式的请求来调试接口. 二.postman安装 ①作为谷歌浏览器插件安装 参考资料: ...

  4. App间相互跳转及图片分享

    A-app: Info--URL Types--URL Schemes:A-app(一个标识,允许别的app调用本App) info.plist 添加白名单: LSApplicationQueries ...

  5. 数位dp-Bomb

    难受啊!!越做题是越感觉菜,这个又被几个坑给卡住了(只有我这个学渣才会卡) 坑点:1.考虑n是否已包含49,有的话还要再+1. 2, 注意从最高开始考虑时,再判断时要考虑它本身为0的情况,.比如n=5 ...

  6. hello1和hello2代码分析

    1.hello1代码分析 hello.java package javaeetutorial.hello1; import javax.enterprise.context.RequestScoped ...

  7. c# ef 排序字段动态,构建动态Lambda和扩展方法OrderBy

    1.动态构建排序 Lambda /// <summary> /// 获取排序Lambda(如果动态排序,类型不同会导致转换失败) /// </summary> /// < ...

  8. 将字符串XX,SS以“,”符号进行区分并分别存储在数组中

    public static void main(String[] args) { String str = "EAN_13,1534651"; String strs[] = st ...

  9. 权限系统设计-day01

    数据库表的设计:   关键流程思考: 权限在SSH系统中应该表现为什么东西? 小胖这个用户登陆:1,检查用户名和密码;2,检查通过; 1),得到小胖这个用户的对应的所有的角色:R1 2),根据所有的角 ...

  10. leetcode-数组-子集

    一.题目描述 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入: nums = [1,2,3] 输出: [ [3], [1], ...