spring–类型转换Converter
文章目录
经过上一篇文章对
PropertyEditor
的学习,相信你也发现了它的一些不足之处
- 线程不安全,
PropertyEditor
使用setValue()
和getValue()
方法设值取值是线程不安全的 PropertyEditor
职责不专一,PropertyEditor
真正和类型转换相关的就4个方法,其余的方法都是和GUI
相关的,不符合接口的设计原则。- 只能进行
String
到其他类型
的转换,已经不太满足spring
的需要了 - 没有使用泛型,未进行严格的类型控制
由于上面这几点原因,迫使spring
自己开发一套类型转换体系Converter
,这个转换器我们就参照spring
官网对它进行学习了。
1 用法
1.1 Converter
接口,1:1
转换
@FunctionalInterface
public interface Converter<S, T> {
/**
* Convert the source object of type {@code S} to target type {@code T}.
* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
* @return the converted object, which must be an instance of {@code T} (potentially {@code null})
* @throws IllegalArgumentException if the source cannot be converted to the desired target type
*/
@Nullable
T convert(S source);
}
这是Converter
体系中最简单的接口,里面只有一个方法convert
,方法实现S
到T
的转换
举个例子,这个Converter
实现String
到Integer
的类型转换
public class StringToIntegerConverter implements Converter<String,Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
1.2 ConverterFactory
接口,1:N
转换
public interface ConverterFactory<S, R> {
/**
* Get the converter to convert from S to target type T, where T is also an instance of R.
* @param <T> the target type
* @param targetType the target type to convert to
* @return a converter from S to T
*/
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
它实际上是一个工厂,能够生产所有S
到R
类型实现类的Converter
,真正的转换逻辑仍然是在Converter
中实现的。
下面这张图是Spring
内部携带的几个ConverterFactory
。
我们就以StringToNumberConverterFactory
为例,看看如何使用这个接口吧。
final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
//获取Converter的时候必须得传入目标类型
@Override
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
//创建String到目标类型的Converter转换器
return new StringToNumber<>(targetType);
}
//真正的转换逻辑交给了这个Converter完成
private static final class StringToNumber<T extends Number> implements Converter<String, T> {
private final Class<T> targetType;
public StringToNumber(Class<T> targetType) {
this.targetType = targetType;
}
@Override
@Nullable
public T convert(String source) {
if (source.isEmpty()) {
return null;
}
//它交给这个工具类完成
return NumberUtils.parseNumber(source, this.targetType);
}
}
}
1.3 GenericConverter
接口,N:N
转换
public interface GenericConverter {
/**
* Return the source and target types that this converter can convert between.
* <p>Each entry is a convertible source-to-target type pair.
* <p>For {@link ConditionalConverter conditional converters} this method may return
* {@code null} to indicate all source-to-target pairs should be considered.
*/
@Nullable
Set<ConvertiblePair> getConvertibleTypes();
/**
* Convert the source object to the targetType described by the {@code TypeDescriptor}.
* @param source the source object to convert (may be {@code null})
* @param sourceType the type descriptor of the field we are converting from
* @param targetType the type descriptor of the field we are converting to
* @return the converted object
*/
@Nullable
Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
/**
* Holder for a source-to-target class pair.
*/
final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
/**
* Create a new source-to-target pair.
* @param sourceType the source type
* @param targetType the target type
*/
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}
public Class<?> getSourceType() {
return this.sourceType;
}
public Class<?> getTargetType() {
return this.targetType;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || other.getClass() != ConvertiblePair.class) {
return false;
}
ConvertiblePair otherPair = (ConvertiblePair) other;
return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
}
@Override
public int hashCode() {
return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
}
@Override
public String toString() {
return (this.sourceType.getName() + " -> " + this.targetType.getName());
}
}
}
很明显,内部类ConvertiblePair
代表了一个source-to-target
类型转换对,转换逻辑由convert
实现。
一般我们并不会直接使用这个接口来完成类型转换,而是使用ConditionalGenericConverter
。这个接口是GenericConverter
和ConditionalConverter
的组合接口,它并没有自己的接口方法
public interface ConditionalConverter {
/**
* Should the conversion from {@code sourceType} to {@code targetType} currently under
* consideration be selected?
* @param sourceType the type descriptor of the field we are converting from
* @param targetType the type descriptor of the field we are converting to
* @return true if conversion should be performed, false otherwise
*/
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
matches
方法判断当前这个ConditionalConverter
能否实现sourceType
到targetType
的类型转换。
接下来我们就来看一下spring
官网对这个接口举的例子ArrayToCollectionConverter
,实现数组转集合。
final class ArrayToCollectionConverter implements ConditionalGenericConverter {
/**
* 转换服务,所有的转换器都交由它进行管理
* GenericConverter转换器的转换逻辑最终由它实现
* 这个东西等下讲,非常重要
*/
private final ConversionService conversionService;
/**
* 只有这一个构造方法
* 必须传入转换服务对象
*/
public ArrayToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object[].class, Collection.class));
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
/**
* 是否可以进行转换
* sourceType或targetType为null,结果为true
* 经过转换服务判定可以转换,结果为true
* sourceType和targetType存在继承关系,结果为true
*/
return ConversionUtils.canConvertElements(
sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
}
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
int length = Array.getLength(source);
//目标结合元素的类型描述
TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
//创建一个length长度的目标类型的集合
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
(elementDesc != null ? elementDesc.getType() : null), length);
//未指定目标集合的元素类型,不需要转换,直接放入集合
if (elementDesc == null) {
for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i);
target.add(sourceElement);
}
}
//对数组中的每一个元素进行转换,然后再放入集合中
else {
for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i);
//使用转换服务进行转换
Object targetElement = this.conversionService.convert(sourceElement,
sourceType.elementTypeDescriptor(sourceElement), elementDesc);
target.add(targetElement);
}
}
return target;
}
}
是否可以进行转换
public static boolean canConvertElements(@Nullable TypeDescriptor sourceElementType,
@Nullable TypeDescriptor targetElementType, ConversionService conversionService) {
if (targetElementType == null) {
// yes
return true;
}
if (sourceElementType == null) {
// maybe
return true;
}
//转换服务能够进行转换
if (conversionService.canConvert(sourceElementType, targetElementType)) {
// yes
return true;
}
//有继承关系也能转换
if (ClassUtils.isAssignable(sourceElementType.getType(), targetElementType.getType())) {
// maybe
return true;
}
// no
return false;
}
2 ConversionService
统一转换服务
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType);
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
这个接口真正来说就两个方法, canConvert
判断能否进行转换,convert
转换。
还有一个接口ConverterRegistry
,它是spring
定义的Converter
注册器。
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);
}
提供注册和删除Converter
的方法。
ConfigurableConversionService
是一个空接口,没有方法。从名字就可以看出来,它表示一个可配的转换服务。GenericConversionService
提供了ConverterRegistry
和ConversionService
的基本实现。DefaultConversionService
会自动注册一批Converter
,为系统提供基本的类型转换能力。
2.1 GenericConversionService
实现注册Converter
下面是GenericConversionService
类的4
个属性
public class GenericConversionService implements ConfigurableConversionService {
//代表不需要使用转换器
private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP");
//代表找不到合适的转换器
private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");
/**
* 内部类,表示统一转换服务持有的所有转换器
* 真正的注册操作委托给这个类实现
*/
private final Converters converters = new Converters();
//缓存,提供快速访问
private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64);
}
2.1.1 Converters
,管理转换服务中所有Converter
的注册
/**
* Manages all converters registered with the service.
*/
private static class Converters {
//未明确指定转换类型的Converter
private final Set<GenericConverter> globalConverters = new LinkedHashSet<>();
/**
* ConvertiblePair这个前面说过,记录了一对source到target的转换
* ConvertersForPair它里面定义了一个GenericConverter的list集合,因为
* source到target的转换可以有多个Converter实现
*/
private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);
/**
* 只有一个添加方法,但是有三种类型的转换器
* Converter、ConverterFactory、GenericConverter
* 其实这是借助了ConverterAdapter,ConverterFactoryAdapter这两个适配器,
* 将Converter、ConverterFactory转换为GenericConverter,方便管理
* ConverterAdapter,ConverterFactoryAdapter是GenericConversionService的内部类
*/
public void add(GenericConverter converter) {
//获取当前转换器能够转换的类型(转换对)集合
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
//没有转换对,就添加到globalConverters中
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) {
//从转换服务中获取转换对对应的转换器集合
ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
//添加进去
convertersForPair.add(converter);
}
}
}
private ConvertersForPair getMatchableConverters(ConvertiblePair convertiblePair) {
//computeIfAbsent方法,如果不存在convertiblePair,就向map集合中添加该项convertiblePair
return this.converters.computeIfAbsent(convertiblePair, k -> new ConvertersForPair());
}
}
在2.2.2
中需要用到一个find
方法,这个方法用来获取转换服务中指定类型转换的转换器
/**
* Find a {@link GenericConverter} given a source and target type.
* <p>This method will attempt to match all possible converters by working
* through the class and interface hierarchy of the types.
* @param sourceType the source type
* @param targetType the target type
* @return a matching {@link GenericConverter}, or {@code null} if none found
*/
@Nullable
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Search the full type hierarchy
//获取给定类所有的层次接结构
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
//双重循环检查转换服务中是否包含一个source->target的GenericConverter
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
//获取source->target转换的GenericConverter
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
/**
* 可以看到,它是首先从converters中获取sourceType->targetType的GenericConverter转换器
* 这里面获取不到的时候才从globalConverters中获取
* globalConverters是一个set集合,不像converters是一个map集合,提供了key,快速获取
* 它需要遍历,调用每一个GenericConverter的matches方法判断能否实现sourceType->targetType的转换
*/
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters
ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
if (convertersForPair != null) {
GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
if (converter != null) {
return converter;
}
}
// Check ConditionalConverters for a dynamic match
for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
return globalConverter;
}
}
return null;
}
2.1.2ConvertersForPair
用来管理指定的ConvertiblePair
对应的所有converters
/**
* Manages converters registered with a specific {@link ConvertiblePair}.
*/
private static class ConvertersForPair {
private final LinkedList<GenericConverter> converters = new LinkedList<>();
//每次新加的都是加在头部
public void add(GenericConverter converter) {
this.converters.addFirst(converter);
}
//获取Converter的时候会调用matches方法进行匹配
@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;
}
}
2.1.3 ResolvableType
可解析的类型
这个类其实在上一篇文章2.4.4.1
章节中有过简单剖析,它是spring
对java.lang.reflect.Type
类型的进一步封装,这个类提供了很多个静态方法简化我们使用反射获取类型信息
forField(Field)
解析属性类型,返回该属性对应的ResolvableType
forMethodParameter(Method, int)
解析方法索引对应的参数类型,返回该参数对应的ResolvableType
forMethodReturnType(Method)
解析方法返回值类型,返回该返回值对应的ResolvableType
forClass(Class)
解析Class
类型,返回该Class
对应的ResolvableType
现在我们来详细看一下它的实现原理
/**
* Return a {@link ResolvableType} for the specified {@link Class},
* using the full generic type information for assignability checks.
* For example: {@code ResolvableType.forClass(MyArrayList.class)}.
* @param clazz the class to introspect ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here)
* @return a {@link ResolvableType} for the specified class
* @see #forClass(Class, Class)
* @see #forClassWithGenerics(Class, Class...)
*/
public static ResolvableType forClass(@Nullable Class<?> clazz) {
//使用私有的构造方法创建一个ResolvableType对象
return new ResolvableType(clazz);
}
/**
* Private constructor used to create a new {@link ResolvableType} on a {@link Class} basis.
* Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper.
* @since 4.2
*/
private ResolvableType(@Nullable Class<?> clazz) {
//resolved(当前解析的类)和type(解析出来的类型)都是clazz
this.resolved = (clazz != null ? clazz : Object.class);
this.type = this.resolved;
this.typeProvider = null;
this.variableResolver = null;
this.componentType = null;
this.hash = null;
}
/**
* Return this type as a {@link ResolvableType} of the specified class. Searches
* {@link #getSuperType() supertype} and {@link #getInterfaces() interface}
* hierarchies to find a match, returning {@link #NONE} if this type does not
* implement or extend the specified class.
* @param type the required type (typically narrowed)
* @return a {@link ResolvableType} representing this object as the specified
* type, or {@link #NONE} if not resolvable as that type
* @see #asCollection()
* @see #asMap()
* @see #getSuperType()
* @see #getInterfaces()
* 这个方法可以获取到clazz父类或者接口的ResolvableType
* 可以发现,最终还是调用了as方法递归获取ResolvableType
*/
public ResolvableType as(Class<?> type) {
if (this == NONE) {
return NONE;
}
//resolve()返回resolved属性值
Class<?> resolved = resolve();
if (resolved == null || resolved == type) {
return this;
}
//接口
for (ResolvableType interfaceType : getInterfaces()) {
ResolvableType interfaceAsType = interfaceType.as(type);
if (interfaceAsType != NONE) {
return interfaceAsType;
}
}
//父类
return getSuperType().as(type);
}
我们来看一下它是如何获取接口和父类
/**
* Return a {@link ResolvableType} array representing the direct interfaces
* implemented by this type. If this type does not implement any interfaces an
* empty array is returned.
* <p>Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}.
* @see #getSuperType()
*/
public ResolvableType[] getInterfaces() {
Class<?> resolved = resolve();
if (resolved == null) {
return EMPTY_TYPES_ARRAY;
}
ResolvableType[] interfaces = this.interfaces;
if (interfaces == null) {
//反射获取接口信息
Type[] genericIfcs = resolved.getGenericInterfaces();
interfaces = new ResolvableType[genericIfcs.length];
for (int i = 0; i < genericIfcs.length; i++) {
//将Type包装为ResolvableType
interfaces[i] = forType(genericIfcs[i], this);
}
this.interfaces = interfaces;
}
return interfaces;
}
/**
* Return a {@link ResolvableType} representing the direct supertype of this type.
* If no supertype is available this method returns {@link #NONE}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
* @see #getInterfaces()
*/
public ResolvableType getSuperType() {
Class<?> resolved = resolve();
if (resolved == null) {
return NONE;
}
try {
//反射获取父类信息
Type superclass = resolved.getGenericSuperclass();
if (superclass == null) {
return NONE;
}
ResolvableType superType = this.superType;
if (superType == null) {
//将Type包装为ResolvableType
superType = forType(superclass, this);
this.superType = superType;
}
return superType;
}
catch (TypeNotPresentException ex) {
// Ignore non-present types in generic signature
return NONE;
}
}
方法逻辑很简单,就是使用标准反射获取接口和父类信息,然后包装为ResolvableType
getRequiredTypeInfo
方法也需要看一下,在我们2.1.6
章节有用到,其实就是获取某个类的泛型信息。
private ResolvableType[] getRequiredTypeInfo(Class<?> converterClass, Class<?> genericIfc) {
//反射获取converterClass类上的genericIfc类的信息
ResolvableType resolvableType = ResolvableType.forClass(converterClass).as(genericIfc);
//获取该类上泛型信息
ResolvableType[] generics = resolvableType.getGenerics();
//泛型必须为2个
if (generics.length < 2) {
return null;
}
//第一个泛型的类型
Class<?> sourceType = generics[0].resolve();
//第二个泛型的类型
Class<?> targetType = generics[1].resolve();
if (sourceType == null || targetType == null) {
return null;
}
return generics;
}
/**
* Return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters of
* this type. If no generics are available an empty array is returned. If you need to
* access a specific generic consider using the {@link #getGeneric(int...)} method as
* it allows access to nested generics and protects against
* {@code IndexOutOfBoundsExceptions}.
* @return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters
* (never {@code null})
* @see #hasGenerics()
* @see #getGeneric(int...)
* @see #resolveGeneric(int...)
* @see #resolveGenerics()
*/
public ResolvableType[] getGenerics() {
if (this == NONE) {
return EMPTY_TYPES_ARRAY;
}
ResolvableType[] generics = this.generics;
if (generics == null) {
if (this.type instanceof Class) {
//反射获取泛型信息
Type[] typeParams = ((Class<?>) this.type).getTypeParameters();
generics = new ResolvableType[typeParams.length];
for (int i = 0; i < generics.length; i++) {
//泛型信息包装为ResolvableType
generics[i] = ResolvableType.forType(typeParams[i], this);
}
}
else if (this.type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
generics = new ResolvableType[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
generics[i] = forType(actualTypeArguments[i], this.variableResolver);
}
}
else {
generics = resolveType().getGenerics();
}
this.generics = generics;
}
return generics;
}
toClass()
方法在2.2.1
章节中用到,它就是获取ResolvableType
解析出来的clazz
/**
* Return this type as a resolved {@code Class}, falling back to
* {@link java.lang.Object} if no specific class can be resolved.
* @return the resolved {@link Class} or the {@code Object} fallback
* @since 5.1
* @see #getRawClass()
* @see #resolve(Class)
* 获取解析出来的类型
*/
public Class<?> toClass() {
//如果类型不能被解析,就返回Object
return resolve(Object.class);
}
/**
* Resolve this type to a {@link java.lang.Class}, returning the specified
* {@code fallback} if the type cannot be resolved. This method will consider bounds
* of {@link TypeVariable TypeVariables} and {@link WildcardType WildcardTypes} if
* direct resolution fails; however, bounds of {@code Object.class} will be ignored.
* @param fallback the fallback class to use if resolution fails
* @return the resolved {@link Class} or the {@code fallback}
* @see #resolve()
* @see #resolveGeneric(int...)
* @see #resolveGenerics()
*/
public Class<?> resolve(Class<?> fallback) {
//如果类型不能被解析,就返回fallback
return (this.resolved != null ? this.resolved : fallback);
}
2.1.4 ConverterAdapter
适配Converter
典型的适配器模式,实现了ConditionalGenericConverter
接口,持有一个Converter
private final class ConverterAdapter implements ConditionalGenericConverter {
//持有一个Converter
private final Converter<Object, Object> converter;
private final ConvertiblePair typeInfo;
private final ResolvableType targetType;
//构造对象的时候需要指定该转换器能够转换的类型
public ConverterAdapter(Converter<?, ?> converter, ResolvableType sourceType, ResolvableType targetType) {
this.converter = (Converter<Object, Object>) converter;
this.typeInfo = new ConvertiblePair(sourceType.toClass(), targetType.toClass());
this.targetType = targetType;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(this.typeInfo);
}
//判断该Converter能否实现sourceType->targetType的转换
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Check raw type first...
if (this.typeInfo.getTargetType() != targetType.getObjectType()) {
return false;
}
// Full check for complex generic type match required?
ResolvableType rt = targetType.getResolvableType();
if (!(rt.getType() instanceof Class) && !rt.isAssignableFrom(this.targetType) &&
!this.targetType.hasUnresolvableGenerics()) {
return false;
}
return !(this.converter instanceof ConditionalConverter) ||
((ConditionalConverter) this.converter).matches(sourceType, targetType);
}
//ConverterAdapter最终调用它持有的那个Converter进行类型转换
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converter.convert(source);
}
}
2.1.5 ConverterFactoryAdapter
适配ConverterFactory
实现了ConditionalGenericConverter
接口,持有一个ConverterFactory
private final class ConverterFactoryAdapter implements ConditionalGenericConverter {
//持有一个ConverterFactory
private final ConverterFactory<Object, Object> converterFactory;
private final ConvertiblePair typeInfo;
public ConverterFactoryAdapter(ConverterFactory<?, ?> converterFactory, ConvertiblePair typeInfo) {
this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
this.typeInfo = typeInfo;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(this.typeInfo);
}
/**
* 这个matches方法首先判断它持有的ConverterFactory是否实现了ConditionalConverter接口
* 如果实现了,就调用接口方法判断是否匹配,匹配才有后续逻辑
* 然后判断生产的Converter是否实现了ConditionalConverter接口,实现了就调用接口方法
* 也就是说会判断两次,ConverterFactory一次,它生产的Converter一次
*/
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
boolean matches = true;
if (this.converterFactory instanceof ConditionalConverter) {
matches = ((ConditionalConverter) this.converterFactory).matches(sourceType, targetType);
}
if (matches) {
Converter<?, ?> converter = this.converterFactory.getConverter(targetType.getType());
if (converter instanceof ConditionalConverter) {
matches = ((ConditionalConverter) converter).matches(sourceType, targetType);
}
}
return matches;
}
//最终调用的是ConverterFactory生产的Converter进行类型转换
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
}
2.1.6 ConverterCacheKey
ConverterCacheKey
类似于ConvertiblePair
,只不过实现了Comparable
接口,增加排序的功能。
private static final class ConverterCacheKey implements Comparable<ConverterCacheKey> {
//ConvertiblePair中直接封装clazz类型
private final TypeDescriptor sourceType;
private final TypeDescriptor targetType;
public ConverterCacheKey(TypeDescriptor sourceType, TypeDescriptor targetType) {
this.sourceType = sourceType;
this.targetType = targetType;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ConverterCacheKey)) {
return false;
}
ConverterCacheKey otherKey = (ConverterCacheKey) other;
return (this.sourceType.equals(otherKey.sourceType)) &&
this.targetType.equals(otherKey.targetType);
}
@Override
public int hashCode() {
return (this.sourceType.hashCode() * 29 + this.targetType.hashCode());
}
@Override
public String toString() {
return ("ConverterCacheKey [sourceType = " + this.sourceType +
", targetType = " + this.targetType + "]");
}
/**
* ResolvableType.toString()方法是获取完全类型名
* 分别比较sourceType和targetType
*/
@Override
public int compareTo(ConverterCacheKey other) {
int result = this.sourceType.getResolvableType().toString().compareTo(
other.sourceType.getResolvableType().toString());
if (result == 0) {
result = this.targetType.getResolvableType().toString().compareTo(
other.targetType.getResolvableType().toString());
}
return result;
}
}
2.1.7 GenericConversionService
实现注册Converter
这是GenericConversionService
类对ConverterRegistry
接口方法的具体实现
/*********************************注册Converter********************************/
/**
* 注册一个Converter到统一转换服务中,适配为ConverterAdapter
* 注册的时候自动解析该Converter适用的转换对
*/
@Override
public void addConverter(Converter<?, ?> converter) {
//获取Converter接口的泛型信息,见2.1.3
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]));
}
/**
* 注册一个Converter到统一转换服务中,适配为ConverterAdapter
* 方法上指定该Converter适用的转换对
*/
@Override
public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) {
addConverter(new ConverterAdapter(
converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType)));
}
/*******************************注册GenericConverter********************************/
/**
* 注册一个GenericConverter到统一转换服务中,包括注册适配的ConverterAdapter,ConverterFactoryAdapter
* GenericConverter内部包含了转换对
* 描述了source到target的转换
*/
@Override
public void addConverter(GenericConverter converter) {
//注册到2.1.1的Converters中
this.converters.add(converter);
invalidateCache();
}
/*******************************注册ConverterFactory***********************************/
/**
* 注册一个ConverterFactory到统一转换服务中,适配为ConverterFactoryAdapter
* 注册的时候自动解析该Converter适用的转换对
*/
@Override
public void addConverterFactory(ConverterFactory<?, ?> factory) {
ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class);
if (typeInfo == null && factory instanceof DecoratingProxy) {
typeInfo = getRequiredTypeInfo(((DecoratingProxy) factory).getDecoratedClass(), ConverterFactory.class);
}
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
"ConverterFactory [" + factory.getClass().getName() + "]; does the class parameterize those types?");
}
addConverter(new ConverterFactoryAdapter(factory,
new ConvertiblePair(typeInfo[0].toClass(), typeInfo[1].toClass())));
}
/****************************删除对应类型转换的所有Converter******************************/
@Override
public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
this.converters.remove(sourceType, targetType);
invalidateCache();
}
2.2 GenericConversionService
实现类型转换
2.2.1 TypeDescriptor
要转换类型的类型描述
这是spring
定义的一个类,其实在上一篇文章中已经简单的说了一下这个类,但是在ConversionService
实现类型转换又用到了,所以我觉得必须开一个小节来看一下它的方法和属性。
首先来看它拥有的属性
public class TypeDescriptor implements Serializable {
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
//静态属性,缓存
private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<>(32);
private static final Class<?>[] CACHED_COMMON_TYPES = {
boolean.class, Boolean.class, byte.class, Byte.class, char.class, Character.class,
double.class, Double.class, float.class, Float.class, int.class, Integer.class,
long.class, Long.class, short.class, Short.class, String.class, Object.class};
static {
for (Class<?> preCachedClass : CACHED_COMMON_TYPES) {
commonTypesCache.put(preCachedClass, valueOf(preCachedClass));
}
}
//要转换的类型
private final Class<?> type;
/**
* 持有一个ResolvableType,见2.1.3
* ResolvableType代表一个可解析的类型
* 内部定义了很多静态方法,简化我们使用反射获取类型信息
*/
private final ResolvableType resolvableType;
private final AnnotatedElementAdapter annotatedElement;
}
其次是一些重要方法
/**
* Create a new type descriptor for an object.
* <p>Use this factory method to introspect a source object before asking the
* conversion system to convert it to some another type.
* <p>If the provided object is {@code null}, returns {@code null}, else calls
* {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
* @param source the source object
* @return the type descriptor
* 静态方法,为source对象创建一个TypeDescriptor类型描述
*/
@Nullable
public static TypeDescriptor forObject(@Nullable Object source) {
/**
* 使用getClass()方法获取source对象的clazz,最终调用另一个静态方法valueOf()
* 来为指定clazz创建一个TypeDescriptor类型描述
*/
return (source != null ? valueOf(source.getClass()) : null);
}
/**
* Create a new type descriptor from the given type.
* <p>Use this to instruct the conversion system to convert an object to a
* specific target type, when no type location such as a method parameter or
* field is available to provide additional conversion context.
* <p>Generally prefer use of {@link #forObject(Object)} for constructing type
* descriptors from source objects, as it handles the {@code null} object case.
* @param type the class (may be {@code null} to indicate {@code Object.class})
* @return the corresponding type descriptor
* 创建一个指定clazz的TypeDescriptor类型描述
*/
public static TypeDescriptor valueOf(@Nullable Class<?> type) {
if (type == null) {
type = Object.class;
}
//先从静态缓存中获取,不存在就使用构造方法重新创建一个TypeDescriptor类型描述
TypeDescriptor desc = commonTypesCache.get(type);
return (desc != null ? desc : new TypeDescriptor(ResolvableType.forClass(type), null, null));
}
从这个valueOf
方法中我们发现它使用构造方法创建一个TypeDescriptor
类型描述
/**
* Create a new type descriptor from a {@link ResolvableType}.
* <p>This constructor is used internally and may also be used by subclasses
* that support non-Java languages with extended type systems. It is public
* as of 5.1.4 whereas it was protected before.
* @param resolvableType the resolvable type
* @param type the backing type (or {@code null} if it should get resolved)
* @param annotations the type annotations
* @since 4.0
*/
public TypeDescriptor(ResolvableType resolvableType, @Nullable Class<?> type, @Nullable Annotation[] annotations) {
this.resolvableType = resolvableType;
/**
* toClass()方法见2.1.3
* 获取ResolvableType解析出来的类型
*/
this.type = (type != null ? type : resolvableType.toClass());
this.annotatedElement = new AnnotatedElementAdapter(annotations);
}
构造方法中初始化了一些值,重点在ResolvableType.forClass(type)
,其实所有的类型解析都是借助ResolvableType
实现的,你可以将TypeDescriptor
理解为对ResolvableType
的更进一步包装。
还有这个getObjectType
方法,如果当前类型描述的类型是基础类型,就得到它的包装类型。2.2.2.2
中用到。
/**
* Variation of {@link #getType()} that accounts for a primitive type by
* returning its object wrapper type.
* <p>This is useful for conversion service implementations that wish to
* normalize to object-based types and not work with primitive types directly.
*/
public Class<?> getObjectType() {
//通过工具类实现
return ClassUtils.resolvePrimitiveIfNecessary(getType());
}
2.2.2 GenericConversionService
实现ConversionService
接口定义的类型转换方法
我们知道GenericConversionService
实现了ConversionService
接口,而ConversionService
提供了类型转换的方法。
2.2.2.1 canConvert
方法,能否进行转换
@Override
public boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
//valueOf方法获取指定类型的TypeDescriptor类型描述
return canConvert((sourceType != null ? TypeDescriptor.valueOf(sourceType) : null),
TypeDescriptor.valueOf(targetType));
}
/**
* 判断能否进行转换就是判断能否获取到sourceType->targetType转换的Converter
*/
@Override
public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
if (sourceType == null) {
return true;
}
//获取sourceType->targetType转换的Converter
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
/**********************************************************************************/
/**
* Hook method to lookup the converter for a given sourceType/targetType pair.
* First queries this ConversionService's converter cache.
* On a cache miss, then performs an exhaustive search for a matching converter.
* If no converter matches, returns the default converter.
* @param sourceType the source type to convert from
* @param targetType the target type to convert to
* @return the generic converter that will perform the conversion,
* or {@code null} if no suitable converter was found
* @see #getDefaultConverter(TypeDescriptor, TypeDescriptor)
*/
@Nullable
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
//见2.1.6,封装一个ConverterCacheKey
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
//先从缓存中获取,没有就从Converters中获取
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
//没有就从Converters中获取,见2.1.1
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;
}
/**
* Return the default converter if no converter is found for the given sourceType/targetType pair.
* <p>Returns a NO_OP Converter if the source type is assignable to the target type.
* Returns {@code null} otherwise, indicating no suitable converter could be found.
* @param sourceType the source type to convert from
* @param targetType the target type to convert to
* @return the default generic converter that will perform the conversion
*/
@Nullable
protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
//转换存在父子关系返回NO_OP_CONVERTER,否则null,见2.1
return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null);
}
2.2.2.2 convert
方法,进行转换
前面两个convert
方法最终调用第三个版本的convert
进行实现,所以核心的转换逻辑在第三个方法,重点看这个方法。
@Override
@SuppressWarnings("unchecked")
@Nullable
public <T> T convert(@Nullable Object source, Class<T> targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
}
/**
* Convenience operation for converting a source object to the specified targetType,
* where the target type is a descriptor that provides additional conversion context.
* Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and
* encapsulates the construction of the source type descriptor using
* {@link TypeDescriptor#forObject(Object)}.
* @param source the source object
* @param targetType the target type
* @return the converted value
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null},
* or sourceType is {@code null} but source is not {@code null}
*/
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor targetType) {
return convert(source, TypeDescriptor.forObject(source), targetType);
}
@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]");
//convertNullSource方法转换Optional空资源
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转换器进行转换
//getConverter方法见2.2.2.1,获取转换服务中的转换sourceType->targetType的转换器
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);
}
下面三个方法在上面用到过,我单独拿出来看一下源码。
/**
* Template method to convert a {@code null} source.
* <p>The default implementation returns {@code null} or the Java 8
* {@link java.util.Optional#empty()} instance if the target type is
* {@code java.util.Optional}. Subclasses may override this to return
* custom {@code null} objects for specific target types.
* @param sourceType the source type to convert from
* @param targetType the target type to convert to
* @return the converted null object
* 转换Optional空资源
*/
@Nullable
protected Object convertNullSource(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
//getObjectType获取基元类型的包装类型
if (targetType.getObjectType() == Optional.class) {
return Optional.empty();
}
return null;
}
//处理基元类型,其实就是不能转换为基元类型
@Nullable
private Object handleResult(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType, @Nullable Object result) {
if (result == null) {
//判断targetType类型是不是一个基元类型
assertNotPrimitiveTargetType(sourceType, targetType);
}
return result;
}
//处理找不到能够转换sourceType->targetType的转换器的情况
@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);
}
/**************************************************************************/
//断言targetType的类型不为基元类型
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"));
}
}
总结一下转换流程
- 首先判断源类型为
null
,那么不用转换,直接返回转换结果为null
或Optional.empty()
。 - 判断源对象不是源类型的实例,抛异常。
- 经过上面两步验证通过后,才去转换服务中获取转换
sourceType->targetType
的GenericConverter
,再用这个GenericConverter
进行转换。 - 找不到对应的转换器,就重新验证最上面两步,否则抛异常
2.3 spring
默认的ConversionService
(DefaultConversionService
)
GenericConversionService
已经实现了统一转换服务的基本功能,但是里面一个转换器都没有,于是spring
就提供了DefaultConversionService
,当我们创建DefaultConversionService
对象的时候,会自动向里面注册一些我们常用的转换器。
我就直接贴上这个类的全部代码了
public class DefaultConversionService extends GenericConversionService {
//内部还持有DefaultConversionService的引用
@Nullable
private static volatile DefaultConversionService sharedInstance;
/**
* Return a shared default {@code ConversionService} instance,
* lazily building it once needed.
* <p><b>NOTE:</b> We highly recommend constructing individual
* {@code ConversionService} instances for customization purposes.
* This accessor is only meant as a fallback for code paths which
* need simple type coercion but cannot access a longer-lived
* {@code ConversionService} instance any other way.
* @return the shared {@code ConversionService} instance (never {@code null})
* @since 4.3.5
* 单例模式,懒汉双重检索获取DefaultConversionService对象
*/
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;
}
/**
* Create a new {@code DefaultConversionService} with the set of
* {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
*/
public DefaultConversionService() {
//注册一些默认的Converter
addDefaultConverters(this);
}
/***********************************默认的Converter******************************/
/**
* Add converters appropriate for most environments.
* @param converterRegistry the registry of converters to add to
* (must also be castable to ConversionService, e.g. being a {@link ConfigurableConversionService})
* @throws ClassCastException if the given ConverterRegistry could not be cast to a ConversionService
*/
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));
}
/**
* Add common collection converters.
* @param converterRegistry the registry of converters to add to
* (must also be castable to ConversionService, e.g. being a {@link ConfigurableConversionService})
* @throws ClassCastException if the given ConverterRegistry could not be cast to a ConversionService
* @since 4.2.3
*/
public static void addCollectionConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
converterRegistry.addConverter(new MapToMapConverter(conversionService));
converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
converterRegistry.addConverter(new StringToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
converterRegistry.addConverter(new StreamConverter(conversionService));
}
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));
converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharsetConverter());
converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCurrencyConverter());
converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}
}
2.4 使用转换服务
首先看spring
中的第一处应用:属性填充
经过上一篇文章的学习,我们知道属性填充阶段的类型转换是由TypeConverterDelegate
类的convertIfNecessary
方法实现的
/**
* Convert the value to the required type (if necessary from a String),
* for the specified property.
* @param propertyName name of the property
* @param oldValue the previous value, if available (may be {@code null})
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or {@code null} if not known, for example in case of a collection element)
* @param typeDescriptor the descriptor for the target property or field
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
@SuppressWarnings("unchecked")
@Nullable
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
/**********************使用ConversionService进行类型转换*******************/
// No custom editor but custom ConversionService specified?
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) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
/**************************PropertyEditor的1:1转换********************************/
Object convertedValue = newValue;
// Value not of required type?
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);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
/************************PropertyEditor的1:N和N:N转换*******************************/
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
if (Object.class == requiredType) {
return (T) convertedValue;
}
else if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor<T> strCtor = requiredType.getConstructor(String.class);
return BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
}
}
}
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && trimmedValue.isEmpty()) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass(
(Number) convertedValue, (Class<Number>) requiredType);
standardConversion = true;
}
}
else {
// convertedValue == null
if (requiredType == Optional.class) {
convertedValue = Optional.empty();
}
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (conversionAttemptEx != null) {
// Original exception from former ConversionService call above...
throw conversionAttemptEx;
}
else if (conversionService != null && typeDescriptor != null) {
// ConversionService not tried before, probably custom editor found
// but editor couldn't produce the required type...
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
}
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type '").append(
ClassUtils.getDescriptiveType(convertedValue)).append("'");
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
if (conversionAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
throw conversionAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
}
return (T) convertedValue;
}
下面来总结一下这个类型转换流程
- 第一步:获取到能够将源类型转换为目标类型的
PropertyEditor
- 第二步:如果得到的
PropertyEditor
为null
,就去判断统一转换服务能否进行该种类型的转换,如果可以的话,就使用这个转换服务进行转换,否则就进入第三步- 第三步:再次判断得到的
PropertyEditor
,如果不为空,就调用PropertyEditor
的setAsText
方法进行类型转换,然后进入第四步,否则直接进入第四步- 第四步:
PropertyEditor
只能进行1:1
的转换,但如果要转换的类型是集合呢?那么就需要对集合中的每一个元素递归进行转换。这一步就是处理1:N
和N:N
转换的
ConversionService
用法很简单
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
//获取源对象的TypeDescriptor类型描述,见2.2.1
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
//canConvert方法判断能否转换
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
//调用convert方法进行转换
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}