类型转换
TypeConverterDelegate
这个类我们只看一个核心方法,如下:
class TypeConverterDelegate { private final PropertyEditorRegistrySupport propertyEditorRegistry; @Nullable private final Object targetObject; public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException { // 查看是否为当前这个类型配置了定制的PropertyEditor PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName); ConversionFailedException conversionAttemptEx = null; // 获取当前容器中的类型转换业务类 ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); // 在这里可以看出,Spring底层在进行类型转换时有两套机制 // 1.首选的是采用PropertyEditor // 2.在没有配置PropertyEditor的情况下,会采用conversionService if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) { TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) { try { // 通过conversionService进行类型转换 return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor); } catch (ConversionFailedException ex) { // fallback to default conversion logic below conversionAttemptEx = ex; } } } Object convertedValue = newValue; // 配置了定制的属性编辑器,采用PropertyEditor进行属性转换 if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) { TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor(); if (elementTypeDesc != null) { Class<?> elementType = elementTypeDesc.getType(); if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) { convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); } } } if (editor == null) { // 没有配置定制的属性编辑器,采用默认的属性编辑器 editor = findDefaultEditor(requiredType); } // 采用属性编辑器进行转换,需要注意的是,默认情况下PropertyEditor只会对String类型的值进行类型转换 convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor); } // ..... return (T) convertedValue; } }
从上面的代码中我们可以知道,Spring在实现类型转换时,有两套机制,第一套机制依赖于PropertyEditor,第二套机制依赖于ConversionService。关于属性编辑器PropertyEditor我们之前已经介绍过了,主要进行的是String到Object的转换,正因为如此,属性编辑器进行类型转换有很大的局限性,所以Spring又推出了一套ConversionService的体系。
ConversionService体系
1、Converter
接口定义
package org.springframework.core.convert.converter; // 将一个S类型的数据转换成T类型 public interface Converter<S, T> { T convert(S source); }
这个接口只能进行一对一的转换,S->T
2、ConverterFactory
接口定义
public interface ConverterFactory<S, R> { <T extends R> Converter<S, T> getConverter(Class<T> targetType); }
利用这个转换工厂,我们可以进行一对多的转换,以Spring内置的一个转换器为例:
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> { @Override public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) { return new StringToEnum(ConversionUtils.getEnumType(targetType)); } private class StringToEnum<T extends Enum> implements Converter<String, T> { private final Class<T> enumType; public StringToEnum(Class<T> enumType) { this.enumType = enumType; } @Override public T convert(String source) { if (source.isEmpty()) { // It's an empty enum identifier: reset the enum value to null. return null; } return (T) Enum.valueOf(this.enumType, source.trim()); } } }
通过传入不同的枚举类型,我们可以从这个工厂中获取到不同的转换器,并把对应的String类型的参数转换成对应的枚举类型数据。
可以看到,通过ConverterFactory,我们能实现一对多的类型转换S->(T extends R)
3、GenericConverter
接口定义
public interface GenericConverter { // 获取能够转换的ConvertiblePair的集合,这个对象就是一组可以进行转换的类型 @Nullable Set<ConvertiblePair> getConvertibleTypes(); // 根据源数据类型转换成目标类型数据 @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType); final class ConvertiblePair { // 源数据类型 private final Class<?> sourceType; // 目标数据类型 private final Class<?> targetType; // .....省略部分代码 } }
相比于前面的Converter以及ConverterFactory,这个接口就更加牛逼了,使用它能完成多对多的转换。因为它内部保存了一个能够进行转换的ConvertiblePair的集合,每个ConvertiblePair代表一组能进行转换的数据类型。同时,这个接口相比我们前面介绍的两个接口,更加的复杂,所以一般情况也不推荐使用这个接口,没有非常必要的话,最好是使用上面两种
一般GenericConverter会与ConditionalGenericConverter配合使用,其接口定义如下:
public interface ConditionalConverter { // 判断是否需要对目标类型转换到原类型,返回true的话代表要执行转换,否则不执行转换 boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); } // 结合了上面两个接口 public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter { }
我们来看下Spring内部的一个实际使用的例子:
final class StringToCollectionConverter implements ConditionalGenericConverter { private final ConversionService conversionService; @Override public Set<ConvertiblePair> getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(String.class, Collection.class)); } @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { return (targetType.getElementTypeDescriptor() == null || // 根据conversionService来判断是否需要执行转换 this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor())); } @Override @Nullable public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { // 这里会借助conversionService来执行转换 } }
可以看到,最终的实现还是借助了ConversionService,那么ConversionService到底是啥呢?
4、ConversionService
接口定义
public interface ConversionService { // 判断是否能进行类型转换 boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType); boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType); // 进行类型转换 @Nullable <T> T convert(@Nullable Object source, Class<T> targetType); @Nullable Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType); }
UML类图
一般来说,实现了ConversionService跟ConverterRegistry会结合使用,对于这种xxxRegistry我相信大家猜都能猜出来它是干什么的了,代码如下:
ConverterRegistry
// 就是在添加Converter或者ConverterFactory public interface ConverterRegistry { void addConverter(Converter<?, ?> converter); <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter); void addConverter(GenericConverter converter); void addConverterFactory(ConverterFactory<?, ?> factory); void removeConvertible(Class<?> sourceType, Class<?> targetType); }
ConfigurableConversionServic
// 单纯的整合了ConversionService以及ConverterRegistry的功能 public interface ConfigurableConversionService extends ConversionService, ConverterRegistry { }
GenericConversionService
这个类已经是一个具体的实现类,可以直接使用,但是我们一般不会直接使用它,而是使用它的子类DefaultConversionService,因为子类提供了很多默认的转换器。
DefaultConversionService
public class DefaultConversionService extends GenericConversionService { @Nullable private static volatile DefaultConversionService sharedInstance; public DefaultConversionService() { addDefaultConverters(this); } public static ConversionService getSharedInstance() { DefaultConversionService cs = sharedInstance; if (cs == null) { synchronized (DefaultConversionService.class) { cs = sharedInstance; if (cs == null) { cs = new DefaultConversionService(); sharedInstance = cs; } } } return cs; } public static void addDefaultConverters(ConverterRegistry converterRegistry) { addScalarConverters(converterRegistry); addCollectionConverters(converterRegistry); converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry)); ...... } public static void addCollectionConverters(ConverterRegistry converterRegistry) { ...... } private static void addScalarConverters(ConverterRegistry converterRegistry) { converterRegistry.addConverterFactory(new NumberToNumberConverterFactory()); ...... } }
相比其父类GenericConversionService,这个子类默认添加了很多的转换器,这样可以极大的方便我们进行开发,所以一般情况下我们都会使用这个类。
如何配置ConversionService
讲了这么多,那么如何往容器中配置一个ConversionService呢?我们需要借助Spring提供的一个ConversionServiceFactoryBean。其代码如下:
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean { @Nullable private Set<?> converters; @Nullable private GenericConversionService conversionService; public void setConverters(Set<?> converters) { this.converters = converters; } @Override public void afterPropertiesSet() { this.conversionService = createConversionService(); ConversionServiceFactory.registerConverters(this.converters, this.conversionService); } protected GenericConversionService createConversionService() { return new DefaultConversionService(); } @Override @Nullable public ConversionService getObject() { return this.conversionService; } @Override public Class<? extends ConversionService> getObjectType() { return GenericConversionService.class; } @Override public boolean isSingleton() { return true; } }
这个类的实现逻辑很简单,ConversionServiceFactoryBean创建完成后,在进行初始化时调用afterPropertiesSet方法,创建一个DefaultConversionService,然后将提供的converters全部注册到这个DefaultConversionService中。所以我们进行如下的配置就行了
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> # 提供自己的converter,可以覆盖默认的配置 <bean class="example.MyCustomConverter"/> </set> </property> </bean>
总结
这篇文章中,我们学习了BeanWrapper,知道一个BeanWrapper其实就是一个Bean的包装器,它对Bean包装的目的是为了能操纵Bean中的属性,所以它同时需要具备获取以及设置Bean中的属性能力,所以它也必须是一个属性访问器(PropertyAccessor),另外为了将各种不同类型的配置数据绑定到Bean的属性上,那么它还得具备属性转换的能力,因为它还得是一个类型转换器(TypeConverter)。
通过上面的分析,我们知道Spring中将类型转换的功能都委托给了一个TypeConverterDelegate,这个委托类在进行类型转换时会有两套方案:
1.PropertyEditor,这是Spring最初提供的方案,扩展了java中的PropertyEditor(java原先提供这个接口的目的更多是为了进行图形化编程)
2.ConversionService,Spring后来提供的一个进行类型转换的体系,用来取代PropertyEditor,因为PropertyEditor有很大的局限性,只能进行String->Object的转换。
画图如下: