本文主要基于 Spring 5.0.6.RELEASE
摘要: 原创出处 http://cmsblogs.com/?p=todo 「小明哥」,谢谢!
作为「小明哥」的忠实读者,「老艿艿」略作修改,记录在理解过程中,参考的资料。
我们知道不管 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 属性的信息和值的对象。
在 #applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)
方法中,会调用 #convertForProperty(Object value, String propertyName, BeanWrapper bw, TypeConverter converter)
进行属性转换,代码如下:
// AbstractAutowireCapableBeanFactory。java |
- 若 TypeConverter 为 BeanWrapperImpl 类型,则使用 BeanWrapperImpl 来进行类型转换,这里主要是因为 BeanWrapperImpl 实现了 PropertyEditorRegistry 接口。
- 否则,调用 TypeConverter 的
#convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
方法,进行类型转换。TypeConverter 是定义类型转换方法的接口,通常情况下与 PropertyEditorRegistry 配合使用实现类型转换。 - 关于 BeanWrapperImpl 小编后续专门出文分析它。
#convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
方法的实现者有两个:DataBinder 和 TypeConverterSupport 类。
- DataBinder 主要用于参数绑定(熟悉 Spring MVC 的都应该知道这个类)
- TypeConverterSupport 则是 TypeConverter 的基本实现,使用的是
typeConverterDelegate
委托者。
所以这里我们只需要关注 TypeConverterSupport 的 #convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
方法,代码如下:
// TypeConverterSupport.java |
我们一直往下跟会跟踪到 TypeConverterDelegate 的 #convertIfNecessary(Object newValue, @Nullable Class<T> requiredType, ...)
方法,会发现如下代码段:
// TypeConverterDelegate.java |
- 如果没有自定义的编辑器则使用 ConversionService 。
ConversionService 是字 Spring 3 后推出来用来替代 PropertyEditor 转换模式的转换体系,接口定义如下:
// ConversionService.java |
其 UML 类图如下:
- ConfigurableConversionService:ConversionService 的配置接口,继承 ConversionService 和 ConverterRegistry 两个接口,用于合并他们两者的操作,以便于通过 add 和 remove 的方式添加和删除转换器。
- GenericConversionService:ConversionService 接口的基础实现,适用于大部分条件下的转换工作,通过 ConfigurableConversionService 接口间接地将 ConverterRegistry 实现为注册 API 。
- DefaultConversionService:ConversionService 接口的默认实现,适用于大部分条件下的转换工作。
回归到 TypeConverterDelegate 的 #convertIfNecessary(String propertyName, Object oldValue, @Nullable Object newValue, Class<T> requiredType, TypeDescriptor typeDescriptor)
方法,在该方法中,如果没有自定义的属性编辑器,则调用 ConversionService 接口的 #convert(...)
,方法定义如下:
// ConversionService.java |
source
:要转换的源对象,可以为null
。sourceType
:source
的类型的上下文,如果source
为null
,则可以为null
。targetType
:source
要转换的类型的上下文。
#convert(...)
方法,将给定的源对象 source
转换为指定的 targetType
。TypeDescriptors 提供有关发生转换的源位置和目标位置的附加上下文,通常是对象字段或属性位置。该方法由子类 GenericConversionService 实现:
// GenericConversionService.java |
<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)
方法,执行转换。代码如下:// ConversionUtils.java
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)
方法,处理结果。代码如下:// GenericConversionService.java
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
为空的情况。代码如下:// GenericConversionService.java
private Object handleConverterNotFound(
@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
// 情况一,如果 source 为空,则返回空
if (source == null) {
assertNotPrimitiveTargetType(sourceType, targetType);
return null;
}
// 情况二,如果 sourceType 为空,或者 targetType 是 sourceType 的子类,则返回 source
if ((sourceType == null || sourceType.isAssignableTo(targetType)) &&
targetType.getObjectType().isInstance(source)) {
return source;
}
// 抛出 ConverterNotFoundException 异常
throw new ConverterNotFoundException(sourceType, targetType);
}
😈 逻辑有点点长,我们先从 #getConverter(TypeDescriptor sourceType, TypeDescriptor targetType)
方法,获取 GenericConverter 对象 converter
。代码如下:
// GenericConversionService.java |
- 这段代码意图非常明确,从
converterCache
缓存中获取,如果存在返回,否则从converters
中获取,然后加入到converterCache
缓存中。 converterCache
和converters
是 GenericConversionService 维护的两个很重要的对象,代码如下:// GenericConversionService.java
/**
* 所有 Converter 集合的封装对象
*/
private final Converters converters = new Converters();
/**
* GenericConverter 缓存
*/
private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64);
Converters 是 GenericConversionService 内部类,用于管理所有注册的转换器,其内部维护一个 Set 和 Map 的数据结构用于管理转换器,代码如下:
// GenericConversionService.java#Converters |
- 同时提供了相应的方法(如 add、remove)操作这两个集合。
在 #getConverter(TypeDescriptor sourceType, TypeDescriptor targetType)
方法中,如果缓存 converterCache
中不存在,则调用 Converters 对象的 #find(TypeDescriptor sourceType, TypeDescriptor targetType)
方法,查找相应的 GenericConverter,如下:
// GenericConversionService.java#Converters |
- 在
#find(TypeDescriptor sourceType, TypeDescriptor targetT)
方法中,会根据sourceType
和targetType
去查询 Converters 中维护的 Map 中是否包括支持的注册类型。如果存在返回 GenericConverter ,如果没有存在返回null
。
当得到 GenericConverter 后,则调用其 #convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType)
方法,进行类型转换。代码如下:
// GenericConverter.java |
到这里我们就可以得到 Bean 属性定义的真正类型了。
GenericConverter 接口
GenericConverter 是一个转换接口,一个用于在两种或更多种类型之间转换的通用型转换器接口。它是 Converter SPI 体系中最灵活的,也是最复杂的接口,灵活性在于 GenericConverter 可以支持在多个源/目标类型对之间进行转换,同时也可以在类型转换过程中访问源/目标字段上下文。由于该接口足够复杂,所有当更简单的 Converter 或 ConverterFactory 接口足够使用时,通常不应使用此接口。其定义如下:
// GenericConverter.java |
GenericConverter 的子类有这么多(看类名就知道是干嘛的了):
我们看一个子类的实现 StringToArrayConverter,该子类将逗号分隔的 String 转换为 Array 。代码如下:
// StringToArrayConverter.java |
在类型转换体系中,Spring 提供了非常多的类型转换器,除了上面的 GenericConverter,还有 Converter、ConditionalConverter、ConverterFactory。
Converter
Converter 是一个将 <S>
类型的源对象转换为 <T>
类型的目标对象的转换器。该接口是线程安全的,所以可以共享。代码如下:
// Converter.java |
子类如下:
ConditionalConverter
ConditionalConverter 接口用于表示有条件的类型转换,通过转入的sourceType
与 targetType
判断转换能否匹配,只有可匹配的转换才会调用convert 方法进行转换。代码如下:
// ConditionalConverter.java |
ConditionalConverter 的子类如下:
ConverterFactory
一个用于“远程”转换的转换工厂,可以将对象从 <S>
转换为 <R>
的子类型。代码如下:
// ConverterFactory.java |
子类如下:
四种不同的转换器承载着不同的转换过程:
- 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)
方法,来看看它是如何完成转换器的注入的工作的。代码如下:
// GenericConversionService.java |
<1>
首先,根据converter
获取 ResolvableType 数组。<2>
然后,将其与converter
封装成一个 ConverterAdapter 实例。<2>
最后,调用#addConverter(GenericConverter converter)
方法,添加到converters
中。- ResolvableType 用于封装 Java 的 Type 类型。
- ConverterAdapter 则是 Converter 的一个适配器, 它实现了 GenericConverter 和 ConditionalConverter 两个类型转换器。
其中,#addConverter(GenericConverter converter)
方法,代码如下:
// GenericConversionService.java |
直接调用内部类 Converters 的 #add(GenericConverter converter)
方法,代码如下:
// GenericConversionService.java |
- 首先调用 GenericConverter 的
#getConvertibleTypes()
方法,获取 ConvertiblePair 集合。如果为空,则加入到globalConverters
集合中,否则通过迭代的方式依次添加converters
中。 ConvertiblePair 为 source-to-target 的持有者,它持有
source
和target
的 class 类型,代码如下:// GenericConverter.java#ConvertiblePair
final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
// 省略其他代码
}
在迭代过程中会根据 ConvertiblePair 获取相应的 ConvertersForPair 对象,然后添加 converter
转换器加入其中。ConvertiblePair 用于管理使用特定GenericConverter.ConvertiblePair 注册的转换器。代码如下:
// GenericConversionService.java#ConvertersForPair |
- 其实内部就是维护一个 LinkedList 集合。他内部有两个方法:
#add(GenericConverter converter)
和getConverter(TypeDescriptor sourceType, TypeDescriptor targetType)
,实现较为简单,这里就不多介绍了。
DefaultConversionService
DefaultConversionService 是 ConversionService 的默认实现,它继承 GenericConversionService,GenericConversionService 主要用于转换器的注册和调用,DefaultConversionService 则是为 ConversionService 体系提供一些默认的转换器。
在 DefaultConversionService 构造方法中就会添加默认的 Converter ,代码如下:
// DefaultConversionService.java |
当然它还提供了一些其他的方法如 #addCollectionConverters(ConverterRegistry converterRegistry)
、addScalarConverters(ConverterRegistry converterRegistry)
方法,用于注册其他类型的转换器。
至此,从 Bean 属性的转换,到 Spring ConversionService 体系的转换器 Converter 以及转换器的管理都介绍完毕了,下篇我们将分析如何利用 ConversionService 实现自定义类型转换器。
艿艿:因为本文是基于调用的过程,进行解析。所以胖友可以自己在总结整理下。
实际上,大体的调用流是如下:
TypeConverterSupport => ConversionService => Converter