本文最后更新于:2022年6月11日 下午
IoC 之深入分析 Bean 的类型转换体系
不管 Bean 对象里面的属性是什么类型,它们都是通过 XML 、Properties 或者其他方式来配置这些属性 对象类型的。在 Spring 容器加载过程中,这些属性都是以 String 类型加载进容器的,但是最终都需要将这些 String 类型的属性转换为 Bean 对象属性所对应真正的类型,要想完成这种由字符串到具体对象的转换,就需要这种转换规则相关的信息,而这些信息以及转换过程由 Spring 类型转换体系 来完成。
以 XML 为例,在 Spring 容器加载阶段,容器将 xml 文件中定义的 <bean>
解析为 BeanDefinition,BeanDefinition 中存储着我们定义一个 bean 需要的所有信息,包括属性,这些属性是以 String 类型的存储的。当用户触发 Bean 实例化阶段时,Spring 容器会将这些属性转换为这些属性真正对应的类型。我们知道在 Bean 实例化阶段,属性的注入是在实例化 Bean 阶段的属性注入阶段,即 AbstractAutowireCapableBeanFactory 的 populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)
方法。
在 populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)
方法中,会将 BeanDefinition 中定义的属性值翻译为 PropertyValue ,然后调用 applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)
方法,进行属性应用。其中 PropertyValue 用于保存单个 bean 属性的信息和值的对象。
convertForProperty()
方法在 applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)
方法中,会调用 convertForProperty(Object value, String propertyName, BeanWrapper bw, TypeConverter converter)
进行属性转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Nullable private Object convertForProperty ( @Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) { if (converter instanceof BeanWrapperImpl) { return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName); } else { PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam); } }
若 TypeConverter 为 BeanWrapperImpl 类型,则使用 BeanWrapperImpl 来进行类型转换,这里主要是因为 BeanWrapperImpl 实现了 PropertyEditorRegistry 接口。
否则,调用 TypeConverter 的 convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam)
方法,进行类型转换。TypeConverter 是定义类型转换方法的接口,通常情况下与 PropertyEditorRegistry 配合使用实现类型转换。
convertIfNecessary()
方法convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam)
方法的实现者有两个:DataBinder 和 TypeConverterSupport 类。
DataBinder 主要用于参数绑定(熟悉 Spring MVC 的都应该知道这个类)
TypeConverterSupport 则是 TypeConverter 的基本实现,使用的是 typeConverterDelegate
这里只需要关注 TypeConverterSupport 的 convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam)
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override @Nullable public <T> T convertIfNecessary (@Nullable Object value, @Nullable Class<T> requiredType, @Nullable MethodParameter methodParam) throws TypeMismatchException { return convertIfNecessary(value, requiredType,(methodParam != null ? new TypeDescriptor (methodParam) : TypeDescriptor.valueOf(requiredType))); }@Nullable @Override public <T> T convertIfNecessary (@Nullable Object value, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException { Assert.state(this .typeConverterDelegate != null , "No TypeConverterDelegate" ); try { return this .typeConverterDelegate.convertIfNecessary(null , null , value, requiredType, typeDescriptor); } catch (ConverterNotFoundException | IllegalStateException ex) { throw new ConversionNotSupportedException (value, requiredType, ex); } catch (ConversionException | IllegalArgumentException ex) { throw new TypeMismatchException (value, requiredType, ex); } }
最后执行TypeConverterDelegate 的 convertIfNecessary(Object newValue, @Nullable Class requiredType, ...)
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Nullable public <T> T convertIfNecessary (@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException { ConversionService conversionService = this .propertyEditorRegistry.getConversionService(); if (editor == null && conversionService != null && newValue != null && typeDescriptor != null ) { TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) { try { return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor); } catch (ConversionFailedException ex) { conversionAttemptEx = ex; } } } }
ConversionService 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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) ; }
ConfigurableConversionService :ConversionService 的配置接口,继承 ConversionService 和 ConverterRegistry 两个接口,用于合并他们两者的操作,以便于通过 add 和 remove 的方式添加和删除转换器。
GenericConversionService :ConversionService 接口的基础实现,适用于大部分条件下的转换工作,通过 ConfigurableConversionService 接口间接地将 ConverterRegistry 实现为注册 API 。
DefaultConversionService :ConversionService 接口的默认实现,适用于大部分条件下的转换工作。
回到 TypeConverterDelegate 的 convertIfNecessary(String propertyName, Object oldValue, @Nullable Object newValue, Class requiredType, TypeDescriptor typeDescriptor)
方法,在该方法中,如果没有自定义的属性编辑器,则调用 ConversionService 接口的 convert(...)
:
1 2 3 Object convert (@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) ;
source
:要转换的源对象,可以为 null
。
sourceType
:source
的类型的上下文,如果 source
为 null
,则可以为 null
。
targetType
:source
要转换的类型的上下文。
convert(...)
方法,将给定的源对象 source
转换为指定的 targetType
。TypeDescriptors 提供有关发生转换的源位置和目标位置的附加上下文,通常是对象字段或属性位置。该方法由子类 GenericConversionService 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Override @Nullable public Object convert (@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { Assert.notNull(targetType, "Target type to convert to cannot be null" ); if (sourceType == null ) { Assert.isTrue(source == null , "Source must be [null] if source type == [null]" ); return handleResult(null , targetType, convertNullSource(null , targetType)); } if (source != null && !sourceType.getObjectType().isInstance(source)) { throw new IllegalArgumentException ("Source to convert from must be an instance of [" + sourceType + "]; instead it was a [" + source.getClass().getName() + "]" ); } GenericConverter converter = getConverter(sourceType, targetType); if (converter != null ) { Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType); return handleResult(sourceType, targetType, result); } return handleConverterNotFound(source, sourceType, targetType); }
<1>
处,如果 sourceType
为空,则直接处理结果。
<2>
处,如果类型不对,抛出 IllegalArgumentException 异常。
<3>
处,调用 getConverter(TypeDescriptor sourceType, TypeDescriptor targetType)
方法,获取 GenericConverter 对象 converter
。
<4>
处,如果 converter
非空,则进行转换,然后再处理结果。
<4.1>
处,调用 ConversionUtils#invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType, TypeDescriptor targetType)
方法,执行转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Nullable public static Object invokeConverter (GenericConverter converter, @Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { try { return converter.convert(source, sourceType, targetType); } catch (ConversionFailedException ex) { throw ex; } catch (Throwable ex) { throw new ConversionFailedException (sourceType, targetType, source, ex); } }
<4.2>
处,调用 handleResult(TypeDescriptor sourceType, TypeDescriptor targetType, Object result)
方法,处理结果,实际上就是校验 结果。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Nullable private Object handleResult (@Nullable TypeDescriptor sourceType, TypeDescriptor targetType, @Nullable Object result) { if (result == null ) { assertNotPrimitiveTargetType(sourceType, targetType); } return result; }private void assertNotPrimitiveTargetType (@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { if (targetType.isPrimitive()) { throw new ConversionFailedException (sourceType, targetType, null , new IllegalArgumentException ("A null value cannot be assigned to a primitive type" )); } }
<5>
处,调用 handleConverterNotFound(Object source, TypeDescriptor sourceType, TypeDescriptor targetType)
方法,处理 converter
为空的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Nullable private Object handleConverterNotFound ( @Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null ) { assertNotPrimitiveTargetType(sourceType, targetType); return null ; } if ((sourceType == null || sourceType.isAssignableTo(targetType)) && targetType.getObjectType().isInstance(source)) { return source; } throw new ConverterNotFoundException (sourceType, targetType); }
getConverter()
方法先从 getConverter(TypeDescriptor sourceType, TypeDescriptor targetType)
方法,获取 GenericConverter 对象 converter
开始,即上边**<3>**。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 private final Converters converters = new Converters ();private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap <>(64 );@Nullable protected GenericConverter getConverter (TypeDescriptor sourceType, TypeDescriptor targetType) { ConverterCacheKey key = new ConverterCacheKey (sourceType, targetType); GenericConverter converter = this .converterCache.get(key); if (converter != null ) { return (converter != NO_MATCH ? converter : null ); } converter = this .converters.find(sourceType, targetType); if (converter == null ) { converter = getDefaultConverter(sourceType, targetType); } if (converter != null ) { this .converterCache.put(key, converter); return converter; } this .converterCache.put(key, NO_MATCH); return null ; }
逻辑比较简单:从 converterCache
缓存中获取,如果存在返回,否则从 converters
中获取,然后加入到 converterCache
缓存中。
Converters 是 GenericConversionService 内部类,用于管理所有注册的转换器,其内部维护一个 Set 和 Map 的数据结构用于管理转换器:
1 2 3 4 private final Set<GenericConverter> globalConverters = new LinkedHashSet <>();private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap <>(36 );
在 getConverter(TypeDescriptor sourceType, TypeDescriptor targetType)
方法中,如果缓存 converterCache
中不存在,则调用 Converters 对象的 find(TypeDescriptor sourceType, TypeDescriptor targetType)
方法,查找相应的 GenericConverter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @Nullable public GenericConverter find (TypeDescriptor sourceType, TypeDescriptor targetType) { List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType()); List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType()); for (Class<?> sourceCandidate : sourceCandidates) { for (Class<?> targetCandidate : targetCandidates) { ConvertiblePair convertiblePair = new ConvertiblePair (sourceCandidate, targetCandidate); GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair); if (converter != null ) { return converter; } } } return null ; }@Nullable private GenericConverter getRegisteredConverter (TypeDescriptor sourceType, TypeDescriptor targetType, ConvertiblePair convertiblePair) { ConvertersForPair convertersForPair = this .converters.get(convertiblePair); if (convertersForPair != null ) { GenericConverter converter = convertersForPair.getConverter(sourceType, targetType); if (converter != null ) { return converter; } } for (GenericConverter globalConverter : this .globalConverters) { if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) { return globalConverter; } } return null ; }
在 find(TypeDescriptor sourceType, TypeDescriptor targetT)
方法中,会根据 sourceType
和 targetType
去查询 Converters 中维护的 Map 中是否包括支持的注册类型。如果存在返回 GenericConverter ,如果没有存在返回 null
。
convert()
方法当得到 GenericConverter 后,则调用其 convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType)
方法,进行类型转换,即**<4.1>**处。
1 2 Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
该方法内部调用的就是GenericConverter.convert()
,这里就可以得到 Bean 属性定义的真正类型了。
GenericConverter
接口GenericConverter 是一个转换接口,一个用于在两种或更多种类型之间转换的通用型转换器接口。它是 Converter SPI 体系中最灵活的,也是最复杂的接口,灵活性在于 GenericConverter 可以支持在多个源/目标类型对之间进行转换,同时也可以在类型转换过程中访问源/目标字段上下文。由于该接口足够复杂,所有当更简单的 Converter 或 ConverterFactory 接口足够使用时,通常不应使用此接口:
1 2 3 4 5 6 7 8 public interface GenericConverter { @Nullable Set<ConvertiblePair> getConvertibleTypes () ; @Nullable Object convert (@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) ; }
在类型转换体系中,Spring 提供了非常多的类型转换器,除了上面的 GenericConverter,还有 Converter、ConditionalConverter、ConverterFactory。
Converter
接口Converter 是一个将 类型的源对象转换为
类型的目标对象的转换器。该接口是线程安全的,所以可以共享:
1 2 3 4 5 6 public interface Converter <S, T> { @Nullable T convert (S source) ; }
ConditionalConverter
接口ConditionalConverter 接口用于表示有条件的类型转换,通过转入的sourceType
与 targetType
判断转换能否匹配,只有可匹配的转换才会调用convert 方法进行转换。
1 2 3 4 public interface ConditionalConverter { boolean matches (TypeDescriptor sourceType, TypeDescriptor targetType) ; }
ConverterFactory
接口一个用于“远程”转换的转换工厂,可以将对象从 转换为
的子类型:
1 2 3 4 5 public interface ConverterFactory <S, R> { <T extends R > Converter<S, T> getConverter (Class<T> targetType) ; }
四种不同的转换器承载着不同的转换过程:
Converter:用于 1:1
的 source -> target
类型转换。
ConverterFactory:用于 1:N
的 source -> target
类型转换。
GenericConverter用于 N:N
的 source -> target
类型转换。
ConditionalConverter:有条件的 source -> target
类型转换。
GenericConversionService()
接口ConversionService 接口中定义了两类方法:
canConvert(sourceType, targetType)
方法,用于判 sourceType
能否转成 targetType
。
convert(source, targetType)
方法,用于将 source
转成转入的 TargetType 类型实例。
这两类方法都是在 GenericConversionService 中实现。 类 GenericConversionService 实现 ConfigurableConversionService 接口,而 ConfigurableConversionService 接口继承 ConversionService 和 ConverterRegistry。 ConverterRegistry 提供了类型转换器的管理功能,他提供了四个 add 和一个 remove 方法,支持注册/删除相应的类型转换器。
GenericConversionService 作为一个基础实现类,它即支持了不同类型之间的转换,也对各类型转换器进行管理,主要是通过一个 Map 类型的 converterCache
和一个内部类 Converters 。在上面已经分析了 GenericConversionService 执行类型转换的过程 cover(...)
方法。下面我们就一个 addConverter(Converter converter)
方法,来看看它是如何完成转换器的注入的工作的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public void addConverter (Converter<?, ?> converter) { ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class); if (typeInfo == null && converter instanceof DecoratingProxy) { typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class); } if (typeInfo == null ) { throw new IllegalArgumentException ("Unable to determine source type <S> and target type <T> for your Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?" ); } addConverter(new ConverterAdapter (converter, typeInfo[0 ], typeInfo[1 ])); }
<1>
首先,根据 converter
获取 ResolvableType 数组。
<2>
然后,将其与 converter
封装成一个 ConverterAdapter 实例。
<2>
最后,调用 addConverter(GenericConverter converter)
方法,添加到 converters
中。
ResolvableType 用于封装 Java 的 Type类型。
ConverterAdapter 则是 Converter 的一个适配器, 它实现了 GenericConverter 和 ConditionalConverter 两个类型转换器。
addConverter(GenericConverter converter)
方法:
1 2 3 4 5 6 7 8 9 @Override public void addConverter (GenericConverter converter) { this .converters.add(converter); invalidateCache(); }
直接调用内部类 Converters 的 add(GenericConverter converter)
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void add (GenericConverter converter) { Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes(); if (convertibleTypes == null ) { Assert.state(converter instanceof ConditionalConverter, "Only conditional converters may return null convertible types" ); this .globalConverters.add(converter); } else { for (ConvertiblePair convertiblePair : convertibleTypes) { getMatchableConverters(convertiblePair).add(converter); } } }
首先调用 GenericConverter 的 getConvertibleTypes()
方法,获取 ConvertiblePair 集合。如果为空,则加入到 globalConverters
集合中,否则通过迭代的方式依次添加 converters
中。
ConvertiblePair 为 source-to-target 的持有者,它持有 source
和 target
的 class 类型,
1 2 3 4 5 6 7 final class ConvertiblePair { private final Class<?> sourceType; private final Class<?> targetType; }
在迭代过程中会根据 ConvertiblePair 获取相应的 ConvertersForPair 对象,然后添加 converter
转换器加入其中。ConvertiblePair 用于管理使用特定GenericConverter.ConvertiblePair 注册的转换器,也是个内部类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private static class ConvertersForPair { private final LinkedList<GenericConverter> converters = new LinkedList <>(); public void add (GenericConverter converter) { this .converters.addFirst(converter); } @Nullable public GenericConverter getConverter (TypeDescriptor sourceType, TypeDescriptor targetType) { for (GenericConverter converter : this .converters) { if (!(converter instanceof ConditionalGenericConverter) || ((ConditionalGenericConverter) converter).matches(sourceType, targetType)) { return converter; } } return null ; } }
其实内部就是维护一个 LinkedList 集合。他内部有两个方法:add(GenericConverter converter)
和 getConverter(TypeDescriptor sourceType, TypeDescriptor targetType)
,实现较为简单。
DefaultConversionService
DefaultConversionService 是 ConversionService 的默认实现,它继承 GenericConversionService,GenericConversionService 主要用于转换器的注册和调用,DefaultConversionService 则是为 ConversionService 体系提供一些默认的转换器。在 DefaultConversionService 构造方法中就会添加默认的 Converter :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public DefaultConversionService () { addDefaultConverters(this ); }public static void addDefaultConverters (ConverterRegistry converterRegistry) { addScalarConverters(converterRegistry); addCollectionConverters(converterRegistry); converterRegistry.addConverter(new ByteBufferConverter ((ConversionService) converterRegistry)); converterRegistry.addConverter(new StringToTimeZoneConverter ()); converterRegistry.addConverter(new ZoneIdToTimeZoneConverter ()); converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter ()); converterRegistry.addConverter(new ObjectToObjectConverter ()); converterRegistry.addConverter(new IdToEntityConverter ((ConversionService) converterRegistry)); converterRegistry.addConverter(new FallbackObjectToStringConverter ()); converterRegistry.addConverter(new ObjectToOptionalConverter ((ConversionService) converterRegistry)); }
总结 Bean的属性转换大致调用流程:
TypeConverterSupport => ConversionService => Converter