首先说明:以版本为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. DirBuster工具扫描敏感文件

    DirBuster是一个多线程Java应用程序,旨在强制Web/应用程序服务器上的目录和文件名.它可以选择执行纯暴力,在查询隐藏文件和目录方面非常好用. 1)安装DirBuster 前提:电脑中必须安 ...

  2. python多线程和多进程使用

    # 多线程 from concurrent.futures import ThreadPoolExecutor # 多进程 from concurrent.futures import Process ...

  3. Vue 入门. 如何在HTML代码里面快速使用Vue

    概述 browserify是一个 CommonJS风格的模块管理和打包工具,上一篇我们简单地介绍了Vue.js官方基于browserify构筑的一套开发模板.webpack提供了和browserify ...

  4. Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

  5. centos7防火墙配置

    一.在工作中远程连接经常通过堡垒机连接,不能直接开启防火墙.所以就需要写入配置文件中 编译配置文件   /etc/firewalld/zones/public.xml <?xml version ...

  6. NeuChar 平台使用及开发教程 索引

    什么是 NeuChar? NeuChar 是由盛派(Senparc)团队发布的新一代跨平台服务系统,其中包含了开放的跨平台通讯标准及核心计算模块(Senparc.NeuChar.dll)以及配套的云管 ...

  7. 【RL-TCPnet网络教程】第23章 RL-TCPnet之地址解析协议ARP

    第23章      RL-TCPnet之地址解析协议ARP 本章节为大家讲解ARP(Address Resolution Protocol,地址解析协议),通过前面章节对TCP和UDP的学习,需要大家 ...

  8. Java线程状态Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

    一.线程5种状态 新建状态(New) 新创建了一个线程对象. 就绪状态(Runnable) 线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获 ...

  9. 深入理解Spring Redis的使用 (四)、RedisTemplate执行Redis脚本

    对于Redis脚本使用过的同学都知道,这个主要是为了防止竞态条件而用的.因为脚本是顺序执行的.(不用担心效率问题)比如我在工作用,用来设置考试最高分. 如果还没有用过的话,先去看Redis脚本的介绍, ...

  10. 发一些Java面试题,上海尚学堂Java学员面试遇到的真题,值得学习

    1. 下面哪些是Thread类的方法() A start()       B run()       C exit()       D getPriority() 答案:ABD 解析:看Java AP ...