spring类型转换器(一)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring类型转换器(一)相关的知识,希望对你有一定的参考价值。
参考技术A在spring容器初始化的时候,BeanDefinition中配置的bean的属性值一般都为String类型,如何将String类型转换为Bean中属性对应的类型呢,在这个过程中就需要用到类型转换器了。spring实例化bean过程中对属性值的转换主要是使用BeanWrapperImpl实现的。
首先来看下BeanWrapperImpl的使用
定义一个处理日期的转换器
创建一个BeanWrapperImpl用于包装目标bean(这里来模拟spring的内部实现)。然后注册Date类型的转换器,将值使用DatePropertyEditor转换为Date类型。调用setPropertyValue的时候,给testBean的birthday字段设置一个字符串类型的时间,在实际赋值的过程中会调用到类型转换器将字符串转换为日期类型。
同样可以实现一个String trim的转换器
BeanWrapper支持嵌套属性的赋值,当存在嵌套属性的时候需要设置 setAutoGrowNestedPaths=true。
接下来我们就来研究一下BeanWrapperImpl的实现过程
java.beans包中声明了一个PropertyEditor接口,用于提供对属性的访问和赋值。
PropertyEditor有个默认实现类PropertyEditorSupport,如果要自定义属性转换器,直接继承该类,然后覆写setAsText、getAsText、setValue、getValue方法。在类型转换的时候如果值是字符串会调用setAsText来赋值value,其他情况下会调用setValue来赋值value,然后再调用getValue获取改变后的value值完成类型转换。相当于将PropertyEditor当做一个加工作坊,传进去一个值,返回想要类型的值
注意 :PropertyEditor是线程不安全的,有状态的,因此每次使用时都需要创建一个,不可重用;
首先先看下BeanWrapperImpl的继承图。还有一个跟BeanWrapperImpl平级的实现类DirectFieldAccessor,用于处理getter和setter的字段访问
从图可以看出,BeanWrapperImpl实现了3个顶级接口,提供了对象属性访问、类型转换器的注册和查找、类型转换功能
从继承图可以看到PropertyEditorRegistrySupport实现了PropertyEditorRegistry接口,该类默认实现了类型转换器的注册和查找功能
在PropertyEditorRegistrySupport声明了多个存储结构用于存储不同的类型转换器来源。这里需要注意的是PropertyEditor是存在在Map中的,目标类型作为key,所以对于一个类型只能注册一个PropertyEditor,后面注册的会覆盖前面注册的
同时还看到一个conversionService变量,spring提供了另一种类型转换接口Converter,通过conversionService调用对应的Converter进行类型转换,在PropertyEditorRegistrySupport同样可以注册进来conversionService,用于使用Converter进行类型转换。conversionService的详细使用会在下篇文章中讲到。
接下来来看PropertyEditor的注册过程。
在上面提到过PropertyEditor存在在一个Map中,key是目标类型,那么这个参数propertyPath是干嘛的呢?这是为了给属性名指定专属的类型的转换器。因为一个目标类型只能有一个PropertyEditor的限制。但是有时候确实某个属性的类型转换比较特殊,这个时候就可以给这个属性名单独注册一个类型转换器,不会覆盖其他的哦。在类型转换的时候,会先根据属性名去customEditorsForPath中找可以用的PropertyEditor。
来看PropertyEditor的查找流程
首先根据属性名从customEditorsForPath查找特定的类型转换器
根据类型查找定义的类型转换器,如果没有对应的,则查找父类对应的类型转换器
PropertyEditorRegistrySupport 中默认添加了一些转换器,当调用 getDefaultEditor(requiredType)的时候会进行注册
TypeConverter接口定义了将一个值转换为目标类型的值的功能。在继承图中可以看出TypeConverterSupport对类型转换提供了默认实现。
TypeConverterSupport将类型转换功能委托给typeConverterDelegate实现
TypeConverterDelegate实现了类型转换的功能,创建TypeConverterDelegate的时候需要一个propertyEditorRegistry对象,用于查找匹配的类型转换器
首先通过propertyEditorRegistry查找自定义的类型转换器PropertyEditor和ConversionService
当该类型没有注册自定义的PropertyEditor,并且存在conversionService的时候,使用conversionService进行类型转换。如果conversionService中没有配置对应的converter,那么继续尝试使用默认注册的PropertyEditor。
如果目标类型存在自定义PropertyEditor 或者 目标类型和值类型不一样则需要进行类型转换(当没有找到ProperEditor的时候会尝试查找默认注册的PropertyEditor)
类型转换,主要分三种情况处理,值类型不是字符串,值类型是字符串数组,值类型是字符串
最后,需要对转换后的值和目标类型进行判断,是否符合要求,如果不符合继续处理。这里主要处理了集合类型还有Number类型、基本类型转String、枚举类型。
在attemptToConvertStringToEnum方法中自动根据枚举的名称转换为枚举的对象
PropertyAccessor接口定义了属性访问的功能。通过实现setPropertyValue 和 getPropertyValue方法实现对象属性的赋值和访问。
AbstractPropertyAccessor实现了PropertyAccessor接口,不过没有对setPropertyValue和getPropertyValue进行实现,而是单独提供了一个 setPropertyValues的方法, 用于批量设置属性值,同时可以通过 ignoreUnknown和ignoreInvalid参数忽略未知的属性
在AbstractNestablePropertyAccessor类中实现了setPropertyValue和getPropertyValue功能。
由于存在嵌套属性赋值的情况,对于嵌套的处理,其实只需要对嵌套的最底层进行类型转换,上层每一层就创建默认的值然后set到再上层对象的属性中。在这里spring使用了递归解决这个问题,创建每一层属性的对象值,使用BeanWrapper包装该对象,那么又是一个BeanWrapperImpl的赋值流程。
来看这个递归的内部实现,在getNestedPropertyAccessor完成对外层属性的初始化和将该值赋值到所依赖的对象中。然后使用BeanWrapper封装属性对象,后续走属性对象的赋值流程
创建属性对象,并将该对象set到宿主对象。因为对象是指针引用的,所以在这步已经完成对宿主对象的属性赋值,接下来的流程只要对属性对象中的依赖属性进行赋值。
使用递归解决了嵌套赋值的问题,那么接下来就是针对最底层BeanWrapperImpl的属性赋值流程
在processLocalProperty方法中,首先通过子类获取属性处理器,通过PropertyHandler对属性赋值。在赋值之前再次判断属性是否已经进行了类型转换,如果没有再次调用类型转换器进行转换,如果已经完成类型转换,使用ConvertedValue
对属性的访问和设置spring进行了更小粒度的封装。提供了 PropertyHandler抽象类。为什么在这里进行抽象,看PropertyHandler的两个实现,可以看到一个是BeanPropertyHandler,一个是FieldPropertyHandler,不难想象,属性一种是由getter和setter方法进行访问,一种是没有getter和setter直接反射字段进行的。
如果要对map进行控制,我们可以再提供一个专门处理map的实现了类handler
BeanWrapperImpl提供了BeanPropertyHandler,将setter和getter传入
BeanPropertyHandler提供对setter和getter的访问
AbstractNestablePropertyAccessor的另一个实现类DirectFieldAccessor,专门用于给字段赋值,不依赖setter和getter,那么这个是怎么实现的,看源码发现是DirectFieldAccessor中提供了一个PropertyHandler的实现类,通过Field的反射实现了setValue和getValue
Spring--Spring类型转换
Spring类型转换的实现
- 基于JavaBeans接口的类型转换实现:基于java.beans.PropertyEditor接口扩展
- Spring 3.0+通用类型转换实现
使用场景
基于JavaBeans接口的类型转换
- 核心职责: 将String类型的内容转换为目标类型的对象
- 扩展原理
1.Spring框架将文本内容传递到PropertyEditor实现的setAsText(String)方法。
2.PropertyEditor#setAsText(String)方法实现将String类型转化为目标类型的对象。
3.将目标类型的对象传入PropertyEditor#setValue(Object)方法实现需要临时存储传入对象
4.Spring框架将通过PropertyEditor#getValue()获取类型转换后的对象
Spring内建PropertyEditor扩展
自定义PropertyEditor扩展
- 扩展模式
1.扩展java.beans.PropertyEditorSupport类
- 实现org.springframework.beans.PropertyEditorRegistrar
1.实现registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)方法
2.将PropertyEditorRegistrar实现注册为SpringBean
- 向org.springframework.beans.PropertyEditorRegistry注册自定义PropertyEditor实现
1.通用类型实现registerCustomEditor(Class<?>, PropertyEditor)
2.Java Bean属性类型实现:registerCustomEditor(Class<?>, String, PropertyEditor)
Spring3通用类型转换接口
- 类型转换接口
@FunctionalInterface
public interface Converter<S, T>
@Nullable
T convert(S source);
- 通用类型转换接口
org.springframework.core.conert.converter.GenericConverter
核心方法: convert(Object, TypeDescriptor. TypeDescriptor)
配对类型: org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
类型描述: org.springframework.core.convert.TypeDescriptor
GenericConverter接口
使用场景:用于"复合"类型转换场景,比如Collection, Map数组等
转换范围: Set getConvertibleTypes()
配对类型:org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
转换方法:convert(Object, TypeDescriptor, TypeDescriptor)
类型描述: org.springframework.core.convert.TypeDescriptor
优化GenericConverter接口
增加sourceType和TargetType前置判断,类型条件判断: ConditionalGenericConverter
public interface ConditionalConverter
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
扩展Spring类型转换器
- 实现转换器接口,任一即可
1.org.sringframework.core.convert.convert.converter
2.org.springframework.core.convert.converter.ConverterFactory
3.org.springframework.core.convert.converter.GenericConerter
- 注册转换器实现
1.通过ConversionServiceFactoryBean SpringBean
2.通过org.springframework.core.convert.ConversionService API
统一类型转换服务
使用ConversionService作为依赖
类型转换底层接口-org.springframework.beans.TypeConverter,Spring直接使用此接口实现作为转换实现。
- 核心方法:convertIfNecessary重载方法
- 抽象实现:org.springframework.beans.TypeConverterSupport
- 简单实现: org.springframework.beans.SimpleTypeConverter
以上是关于spring类型转换器(一)的主要内容,如果未能解决你的问题,请参考以下文章
Spring MVC自定义类型转换器Converter参数解析器HandlerMethodArgumentResolver