spring--10--类型转换Converter

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,方法实现ST的转换

举个例子,这个Converter实现StringInteger的类型转换

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);

}

它实际上是一个工厂,能够生产所有SR类型实现类的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。这个接口是GenericConverterConditionalConverter的组合接口,它并没有自己的接口方法

在这里插入图片描述

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能否实现sourceTypetargetType的类型转换。

接下来我们就来看一下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提供了ConverterRegistryConversionService的基本实现。
  • 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章节中有过简单剖析,它是springjava.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,那么不用转换,直接返回转换结果为nullOptional.empty()
  • 判断源对象不是源类型的实例,抛异常。
  • 经过上面两步验证通过后,才去转换服务中获取转换sourceType->targetTypeGenericConverter,再用这个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
  • 第二步:如果得到的PropertyEditornull,就去判断统一转换服务能否进行该种类型的转换,如果可以的话,就使用这个转换服务进行转换,否则就进入第三步
  • 第三步:再次判断得到的PropertyEditor,如果不为空,就调用PropertyEditorsetAsText方法进行类型转换,然后进入第四步,否则直接进入第四步
  • 第四步:PropertyEditor只能进行1:1的转换,但如果要转换的类型是集合呢?那么就需要对集合中的每一个元素递归进行转换。这一步就是处理1:NN: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;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值