SpringMVC类型转换、数据绑定详解
public String method(Integer num, Date birth) {
...
}
Http请求传递的数据都是字符串String类型的,上面这个方法在Controller中定义,如果该方法对应的地址接收到到浏览器的请求的话,并且请求中含有num和birth参数,那么num会被自动转换成Integer对象;birth会被自动转为Date对象(Date转换需要配置属性编辑器)。
本文将分析这一原理,解释SpringMVC是如何实现数据类型的转换。
属性编辑器介绍
在讲解核心内容之前,我们先来了解一下Java中定义的属性编辑器。
sun设计属性编辑器主要是为IDE服务的,让IDE能够以可视化的方式设置JavaBean的属性。
PropertyEditor是属性编辑器的接口。
我们使用属性编辑器一般都是将String对象转换成我们需要的java对象而使用的。
有个方法setAsText很重要。 比如String对象"1"要使用属性编辑器转换成Integer对象,通过setAsText里Integer.parseInt(text)得到Integer对象,然后将Integer对象保存到属性中。
它的基本实现类是PropertyEditorSupport,一般我们要编写自定义的属性编辑器只需要继承这个类即可。
Spring中有很多自定义的属性编辑器,都在spring-beans jar包下的org.springframework.beans.propertyeditors包里。
CustomBooleanEditor继承PropertyEditorSupport并重写setAsText方法。
重要接口和类介绍
刚刚分析了sun设计的属性编辑器。 下面我们来看下Spring对这方面的设计。
1.PropertyEditorRegistry接口
封装方法来给JavaBean注册对应的属性编辑器。
2.PropertyEditorRegistrySupport:PropertyEditorRegistry接口的基础实现类
PropertyEditorRegistrySupport类有个createDefaultEditors方法,会创建默认的属性编辑器。
3.TypeConverter接口
类型转换接口。 通过该接口,可以将value转换为requiredType类型的对象。
4.TypeConverterSupport:TypeConverter基础实现类,并继承了PropertyEditorRegistrySupport
有个属性typeConverterDelegate,类型为TypeConverterDelegate,TypeConverterSupport将类型转换委托给typeConverterDelegate操作。
5.TypeConverterDelegate
类型转换委托类。具体的类型转换操作由此类完成。
6.SimpleTypeConverter
TypeConverterSupport的子类,使用了PropertyEditorRegistrySupport(父类TypeConverterSupport的父类PropertyEditorRegistrySupport)中定义的默认属性编辑器。
7.PropertyAccessor接口
对类中属性操作的接口。
8.BeanWrapper接口
继承ConfigurablePropertyAccessor(继承PropertyAccessor、PropertyEditorRegistry、TypeConverter接口)接口的操作Spring中JavaBean的核心接口。
9.BeanWrapperImpl类
BeanWrapper接口的默认实现类,TypeConverterSupport是它的父类,可以进行类型转换,可以进行属性设置。
10.DataBinder类
实现PropertyEditorRegistry、TypeConverter的类。支持类型转换,参数验证,数据绑定等功能。
有个属性SimpleTypeConverter,用来进行类型转换操作。
11.WebDataBinder
DataBinder的子类,主要是针对Web请求的数据绑定。
部分类和接口测试
由于BeanWrapper支持类型转换,属性设置。以BeanWrapper接口为例,做几个测试,让读者对它们有更清晰的认识:
以TestModel这个JavaBean为例,属性:
private int age;
private Date birth;
private String name;
private boolean good;
private long times;
测试方法1:
TestModel tm = new TestModel();
BeanWrapper bw = new BeanWrapperImpl(tm);
bw.setPropertyValue("good", "on");
//bw.setPropertyValue("good", "1");
//bw.setPropertyValue("good", "true");
//bw.setPropertyValue("good", "yes");
System.out.println(tm);
good是boolean属性,使用BeanWrapperImpl设置属性的时候,内部会使用类型转换(父类TypeConverterSupport提供),将String类型转换为boolean,CustomBooleanEditor对于String值是on,1,true,yes都会转换为true,本文介绍PropertyEditorRegistrySupport的时候说明过,CustomBooleanEditor属于默认的属性编辑器。
测试方法2:
TestModel tm = new TestModel();
BeanWrapperImpl bw = new BeanWrapperImpl(false);
bw.setWrappedInstance(tm);
bw.setPropertyValue("good", "1");
System.out.println(tm);
不使用默认的属性编辑器进行类型转换。 很明显,这段代码报错了,没有找到合适的属性编辑,String类型不能作为boolean类型的值。
错误信息:Failed to convert property value of type 'java.lang.String' to required type 'boolean' for property 'good';
测试方法3:
TestModel tm = new TestModel();
BeanWrapper bw = new BeanWrapperImpl(tm);
bw.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
bw.setPropertyValue("birth", "1990-01-01");
System.out.println(tm);
默认属性编辑器中并没有日期类型的属性编辑器,我们注册一个Spring提供的CustomDateEditor属性编辑器,对应Date对象,并且为空。有了CustomDateEditor,设置birth的时候会通过类型转换自动转化成Date对象。
源码分析
本文所使用的Spring版本是4.0.2
在分析RequestParamMethodArgumentResolver处理请求参数之前,我们简单回顾一下SpringMVC是如何对http请求进行处理的。
HandlerAdapter会对每个请求实例化一个ServletInvocableHandlerMethod对象进行处理,我们仅看下WebDataBinderFactory的构造过程。
WebDataBinderFactory接口是一个创建WebDataBinder的工厂接口。
以如下方法为例:
public ModelAndView test(boolean b, ModelAndView view) {
view.setViewName("test/test");
if(b) {
view.addObject("attr", "b is true");
} else {
view.addObject("attr", "b is false");
}
return view;
}
楼主在另外一篇博客http://www.cnblogs.com/fangjian0423/p/springMVC-request-param-analysis.html中已经分析,boolean类型的参数会被RequestParamMethodArgumentResolver这个HandlerMethodArgumentResolver处理。
下面我们进入RequestParamMethodArgumentResolver看看是如何处理的。
RequestParamMethodArgumentResolver的resolveArgument方法是由它的父类AbstractNamedValueMethodArgumentResolver中定义的:
ServletRequestDataBinderFactory创建ExtendedServletRequestDataBinder。
ExtendedServletRequestDataBinder属于DataBinder的子类。
我们在介绍重要接口的时候说过DataBinder进行类型转换的时候内部会使用SimpleTypeConverter进行数据转换。
下面看看测试:
CustomBooleanEditor处理ohmygod会抛出IllegalArgumentException。 最终被截获处理成http 400错误。
PS:以上例子boolean类型改成Boolean类型的话,不传参数的话b就是null,我们解释默认属性编辑器的时候Boolean类型的参数是允许空的。但是boolean类型不传参数的话,默认会是false,而不会抛出异常。 原因就是resolveArgument方法中handleNullValue处理null值,spring进行了特殊的处理,如果参数类型是boolean的话,取false。 读者可以试试。
再看看个例子:
public ModelAndView testObj(Employee e, ModelAndView view) {
view.setViewName("test/test");
view.addObject("attr", e.toString());
return view;
}
该方法会被ServletModelAttributeMethodProcessorr这个HandlerMethodArgumentResolver处理。
ServletModelAttributeMethodProcessorr的resolveArgument方法是由它的父类ModelAttributeMethodProcessor中定义的:
这里WebDataBinder方法bind中会使用BeanWrapper构造对象,然后设置对应的属性。BeanWrapper本文已介绍过。
编写自定义的属性编辑器
Spring提供的编辑器肯定不会满足我们日常开发的功能,因此开发自定义的属性编辑器也是很有必要的。
下面我们就写1个自定义的属性编辑器。
public class CustomDeptEditor extends PropertyEditorSupport { @Override
public void setAsText(String text) throws IllegalArgumentException {
if(text.indexOf(",") > 0) {
Dept dept = new Dept();
String[] arr = text.split(",");
dept.setId(Integer.parseInt(arr[0]));
dept.setName(arr[1]);
setValue(dept);
} else {
throw new IllegalArgumentException("dept param is error");
}
} }
SpringMVC中使用自定义的属性编辑器有3种方法:
1. Controller方法中添加@InitBinder注解的方法
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Dept.class, new CustomDeptEditor());
}
2. 实现WebBindingInitializer接口
public class MyWebBindingInitializer implements WebBindingInitializer { @Override
public void initBinder(WebDataBinder binder, WebRequest request) {
binder.registerCustomEditor(Dept.class, new CustomDeptEditor());
} }
之前分析源码的时候,HandlerAdapter构造WebDataBinderFactory的时候,会传递HandlerAdapter的属性webBindingInitializer。
因此,我们在配置文件中构造RequestMappingHandlerAdapter的时候传入参数webBindingInitializer。
3. @ControllerAdvice注解
@ControllerAdvice
public class InitBinderControllerAdvice { @InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Dept.class, new CustomDeptEditor());
} }
加上ControllerAdvice别忘记配置文件component-scan需要扫描到这个类。
最终结果:
总结
分析了Spring的数据转换功能,并解释这个神奇的转换功能是如何实现的,之后编写了自定义的属性编辑器。
大致讲解了下Spring类型转换中重要的类及接口。
原文:
https://www.cnblogs.com/fangjian0423/p/springMVC-databind-typeconvert.html
SpringMVC类型转换、数据绑定详解的更多相关文章
- SpringMVC 之类型转换Converter详解转载
SpringMVC之类型转换Converter详解 本文转载 http://www.tuicool.com/articles/uUjaum 1.1 目录 1.1 目录 1.2 ...
- SpringMVC 常用注解 详解
SpringMVC 常用注解 详解 SpringMVC 常用注解 1.@RequestMapping 路径映射 2.@Requ ...
- SpringMvc测试框架详解----服务端测试
随着RESTful Web Service的流行,测试对外的Service是否满足期望也变的必要的.从Spring 3.2开始Spring了Spring Web测试框架,如果版本低于3.2,请使用sp ...
- springMVC的注解详解
springmvc常用注解标签详解 1.@Controller 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业 ...
- SpringMVC异常处理机制详解[附带源码分析]
目录 前言 重要接口和类介绍 HandlerExceptionResolver接口 AbstractHandlerExceptionResolver抽象类 AbstractHandlerMethodE ...
- AngularJS语法基础及数据绑定——详解各种数据绑定指令、属性应用
AngularJS简单易学,但是功能强大.特别是在构建单页面应用方面效果显著.而 数据绑定 可以说是他被广泛使用的最主要的优点.他舍弃了对DOM的操作方式,一切都由AngularJS来自动更新视图,我 ...
- springMVC(1)---@RequestMapping详解
@RequestMapping详解 RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径.这句话,太熟悉了. ...
- springmvc常用注解详解
1.@Controller 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ...
- SpringMVC @RequestMapping 用法详解之地址映射
@RequestMapping 用法详解之地址映射 http://blog.csdn.net/walkerjong/article/details/7994326
- SpringMVC类型转换、数据绑定详解[附带源码分析]
目录 前言 属性编辑器介绍 重要接口和类介绍 部分类和接口测试 源码分析 编写自定义的属性编辑器 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那 ...
随机推荐
- 100以内奇偶数(for循环)
- Java多线程中wait语句的具体使用技巧
Java多线程在使用的时候会有很多语句需要我们具体的学习,在这其中wait()就是其中的一个.当然我们需要不断的努力学习才能掌握这一个语句的应用,下面的代码会对你学习Java多线程有所帮助. clas ...
- C#中的预处理指令详解
这篇文章主要介绍了C#中的预处理指令详解,本文讲解了#define 和 #undef.#if.#elif.#else和#endif.#warning和#error.#region和#endregion ...
- 【AtCoder】ARC083
C - Sugar Water 计算一下可以达到水是多少,可以到达的糖是多少 枚举水,然后加最多能加的糖,是\(min(F - i *100,E * 100)\),计算密度,和前一个比较就行 #inc ...
- python全栈开发day36-IO多路复用
一.复习 1.进程.线程.协程 进程:是计算机中最小的资源分配单位,数据隔离,可以利用多核,数据不安全 线程:是计算机中最小的CPU调度单位,数据共享,GIL,数据不安全 协程:是线程的一部分,是由用 ...
- BZOJ3377 [Usaco2004 Open]The Cow Lineup 奶牛序列 其他
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3377 题意概括 给出一个序列,序列中的数字为1~k中的. 让你求最短的非子序列长度. 题解 我们把 ...
- 第四章: 4.1 logging模块 | 正则表达式
修改json数据然后写入json文件中 f = open('1234.json','r',encoding='utf-8') data = f.read() data1 = json.loads(da ...
- 5.Django|模型层--多表关系
多表操作 文件为 ----> orm2 数据库表关系之关联字段与外键约束 一对多Book id title price publish email addr php 人民出版社 北京 pyth ...
- python--yield and generator(生成器)简述
1.想象一个场景: 设想,我想要100个素数,然后对它们累加求和. 通常的想法是,找一个一次性至少能提供100个素数的工具(函数),让它把这100个素数交给我(用return 一次性返回含 ...
- C++语言实现-拓扑排序
1.拓扑排序的概念 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则 ...