springMVC4(9)属性编辑器剖析入参类型转换原理
来源:互联网 发布:华为数据丢失事件 编辑:程序博客网 时间:2024/05/16 06:11
我们通过Http请求提交的参数都以字符串的形式呈现,但最终在springMVC的方法入参中,我们却能得到各种类型的数据,包括Number、Boolean、复杂对象类型、集合类型、Map类型等,这些都是springMVC内置的数据类型转换器帮我们完成的。springMVC的将请求数据绑定到方法入参的流程如下所示:
在本文里,我们通过属性编辑器来理解springMVC的数据转换、绑定过程。
PropertyEditorRegistrySupport
而对于常见的数据类型,Spring在PropertyEditorRegistrySupport中提供了默认的属性编辑器,这些常见的数据类型如下图所示:
在PropertyEditorRegistrySupport中,有两个重要的Map类型成员变量:
1. private Map<Class<?>, PropertyEditor> defaultEditors
:用于保存默认属性类型的编辑器,元素的key为属性类型,值为对应属性编辑器的实例
2. private Map<Class<?>, PropertyEditor> customEditors
:用于保存用户自定义的属性编辑器,元素的键值和defaultEditors一致。
在PropertyEditorRegistrySupport中,有一个重要的成员方法:createDefaultEditors()来创建默认的属性编辑器,它的定义如下所示:
/** * Actually register the default editors for this registry instance. */private void createDefaultEditors() { //创建一个HashMap存储默认的属性编辑器 this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64); // 简单的属性编辑器,没有参数化功能,在JDK中没有包含下列任意目标类型的编辑器 //这里和我们上表的资源类相对应 this.defaultEditors.put(Charset.class, new CharsetEditor()); this.defaultEditors.put(Class.class, new ClassEditor()); this.defaultEditors.put(Class[].class, new ClassArrayEditor()); this.defaultEditors.put(Currency.class, new CurrencyEditor()); this.defaultEditors.put(File.class, new FileEditor()); this.defaultEditors.put(InputStream.class, new InputStreamEditor()); this.defaultEditors.put(InputSource.class, new InputSourceEditor()); this.defaultEditors.put(Locale.class, new LocaleEditor()); this.defaultEditors.put(Pattern.class, new PatternEditor()); this.defaultEditors.put(Properties.class, new PropertiesEditor()); this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor()); this.defaultEditors.put(TimeZone.class, new TimeZoneEditor()); this.defaultEditors.put(URI.class, new URIEditor()); this.defaultEditors.put(URL.class, new URLEditor()); this.defaultEditors.put(UUID.class, new UUIDEditor()); if (zoneIdClass != null) { this.defaultEditors.put(zoneIdClass, new ZoneIdEditor()); } // 默认的集合类编辑器实例,这里和我们上表的集合类相对应 // 我们能够通过注册自定义的相同类型属性编辑器来重写下面的默认属性编辑器 this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class)); this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class)); this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class)); this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class)); this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class)); // 基本数据的数组类型的默认编辑器 this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor()); this.defaultEditors.put(char[].class, new CharArrayPropertyEditor()); this.defaultEditors.put(char.class, new CharacterEditor(false)); this.defaultEditors.put(Character.class, new CharacterEditor(true)); this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false)); this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true)); // JDK中没有Number包装类的相关属性编辑器 // 通过自定义我们的CustomNumberEditor来重写JDK默认的属性编辑器 this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false)); this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true)); this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false)); this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true)); this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false)); this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true)); this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false)); this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true)); this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false)); this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true)); this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false)); this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true)); this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true)); this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true)); // 只有我们显式将configValueEditorsActive设为true,才会注册下面类型的编辑器 if (this.configValueEditorsActive) { StringArrayPropertyEditor sae = new StringArrayPropertyEditor(); this.defaultEditors.put(String[].class, sae); this.defaultEditors.put(short[].class, sae); this.defaultEditors.put(int[].class, sae); this.defaultEditors.put(long[].class, sae); }}
PropertyEditor
PropertyEditor是Java原生的属性编辑器接口,它的核心功能是将一个字符串转换为一个java对象。
它的定义和常用方法如下所示:
public interface PropertyEditor { //设置属性的值,基本属性类型要以包装类传入 void setValue(Object value); //返回属性的值,基本数据类型会被封装成相应的包装类 Object getValue(); //为属性提供一个表示初始值的字符串,属性编辑器以此值作为属性的默认值 String getJavaInitializationString(); //将属性对象用一个字符串表示,一遍外部的属性编辑器能以可视化的方式显示。 //默认返回null,表示改属性不能以字符串形式表示 String getAsText(); //利用所给字符串text更新属性内部的值 void setAsText(String text) throws java.lang.IllegalArgumentException;}
实例解析自定义属性编辑器
1. 自定义编辑器类
它的一个核心实现类是PropertyEditorSupport,如果我们要编写自定义的属性编辑器,只需要继承这个类,然后重写setAsText方法即可。下面我们来看一个自定义属性编辑器的实例:尝试将字符串“myName,1995-01-01,15k”转换为Person POJO对象,Person对象的定义如下:
package com.mvc.model;import java.util.Date;import org.springframework.format.annotation.DateTimeFormat;import org.springframework.format.annotation.NumberFormat;public class Person { private String name; private Date birthday; private Long salary; //ignore getter and setter @Override public String toString() { return "Person [name=" + name + ", birthday=" + birthday + ", salary=" + salary + "]"; }}
下面是我们自定义的属性编辑器:
public class MyEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { String[] values = text.split(","); Person person = new Person(); person.setName(values[0]); try { person.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse(values[1]));//格式化字符串并解析成日期类型 } catch (ParseException e) { e.printStackTrace(); } person.setSalary(Long.valueOf(values[2].replace("k", "000")));//转换为工资格式 setValue(person);//调用setValue来将我们的Person对象设置为编辑器的属性值 super.setAsText(text); }}
2. 注册编辑器
自定义完属性编辑器后,我们需要将其注册才能生效,SpringMVC中使用自定义的属性编辑器有3种方法:
1. Controller方法中添加@InitBinder注解的方法
实例:
@InitBinderpublic void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Person.class, new MyEditor()); }
2. 实现 WebBindingInitializer接口
方法1是针对特定的控制器的,如果我们需要对全局控制器生效,可以编写自己的WebBindingInitializer,然后在spring容器中注册,如下所示:
public class MyWebBindingInitializer implements WebBindingInitializer { @Override public void initBinder(WebDataBinder binder, WebRequest request) { binder.registerCustomEditor(Dept.class, new CustomDeptEditor()); }}
在容器中注册:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="webBindingInitializer"> <bean class="com.mvc.editor.MyWebBindingInitializer" /> </property></bean>
3. @ControllerAdvice注解
我们可以通过此注解配置一个控制器增强,
@ControllerAdvicepublic class InitBinderControllerAdvice { @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Dept.class, new CustomDeptEditor()); }}
我们需要将其纳入<context:component-scan>
的扫描路径中才能生效。
从上面的分析我们能看到,springMVC注册了大量的数据类型编辑器,恰是通过这些属性编辑器,springMVC帮助我们完成了请求参数字符串到入参数据的绑定。在一篇文章里,我们会谈到SpringMVC对新的转换器框架的支持。
- springMVC4(9)属性编辑器剖析入参类型转换原理
- springMVC4(12)复杂对象和集合类型入参绑定
- NAT类型及转换原理深入剖析
- NAT类型及转换原理深入剖析
- NAT类型及转换原理深入剖析
- springMVC4(3)方法入参灵活绑定
- 剖析Struts2的类型转换
- SpringMVC类型转换器、属性编辑器
- 强制类型转换原理
- springMVC4
- Spring注入Date类型的属性,自定义Date属性编辑器
- Hibernate自定义类型 -类属性类型与数据库类型转换
- springMVC4(10)强大类型转换器实例解析
- Spring自动类型转换/集合属性注入
- Spring MVC Controller 入参类型
- 编码剖析Spring装配基本属性的原理
- 编码剖析Spring装配基本属性的原理
- Effective Javascript (类型转换原理)
- JAVA动态规划(二)--最长公共子序列问题(LCS_subSequence)的三种解法与最长公共子字符串(LCS_subString)的两种解法与最长回文串(LongestPalindrome)
- HDU 1033 Edge
- HDU1075 What Are You Talking About
- 筛法求素数
- HDU1517 巴什博弈变形
- springMVC4(9)属性编辑器剖析入参类型转换原理
- Java虚拟机体系结构
- 1147: 【C语言训练】角谷猜想(其实是3n+1)
- HashMap 原理和源码分析
- [南极星]何处是归处
- IOS --- CocoaPods的安装和使用
- Android编译详解之lunch命令
- 科大讯飞语音集成,非常详细的使用讲解
- 出线后,谈中国足球的苟且与远方