Spring源码解析(十九)-转换体系

寒窗苦读十余载,奋笔疾书若干年

Spring版本

5.2.5.RELEASE

参考

《芋道源码》

源码解读

在之前的文章《Spring源码解析(十)-填充bean属性》中的applyPropertyValues方法,使用到了convertForProperty做了类型转换,本篇,就让我们深入了解一下spring的转换体系。

spring中存在以下几种转换器,他们分别支持不同的转换功能:

  • Converter:用于 1:1 的 source -> target 类型转换。
  • ConverterFactory:用于 1:N 的 source -> target 类型转换。
  • ConditionalConverter:有条件的 source -> target 类型转换。
  • GenericConverter:用于 N:N 的 source -> target 类型转换。

1 Converter

1.1 Demo

在查看源码之前,先从一个例子入手,方便我们debug跟踪源码逻辑

1.1.1 Student

public class Student {

    private String id;

    private String name;

    private String desc;

    private StudentService studentService;

    static class  StudentService{

        private Integer age;
        private String name;

        // 省略getters和setters
    }
    // 省略getters和setters
}

1.1.2 StudentConverter

public class StudentConverter implements Converter<String, StudentService> {

    @Override
    public StudentService convert(String source) {
        if (StringUtils.hasLength(source)) {
            String[] sources = StringUtils.split(source, "#");
            if (sources != null && sources.length >= 2) {
                StudentService student = new StudentService();
                student.setAge(Integer.valueOf(sources[0]));
                student.setName(sources[1]);
                return student;
            }
        }
        return null;
    }
}

1.1.3 spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="studentConverter"/>
            </set>
        </property>
    </bean>

    <bean id="studentConverter" class="com.kungyu.custom.element.StudentConverter"/>

    <bean id="student" class="com.kungyu.custom.element.Student">
        <property name="studentService" value="18#123"/>
    </bean>
</beans>

1.1.4 测试

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getStudentService().getAge());
        System.out.println(student.getStudentService().getName());
}

结果:

18
123

逻辑比较简单,在spring.xml中,我们注册了一个StudentConverter类型的转换器bean,之后将这个转换器bean添加到ConversionServiceFactoryBean的转换器列表converters中。然后声明了一个Student类型的bean,该bean的studentService属性是一个内部类,不过该属性初始参数传入了string类型的18#123,最后通过转换器成功将18#123转换为StudentServiceage属性和name属性,实现了stringStudentService的转换。

1.2 源码

1.2.1 Converter

@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函数,由子类去具体实现

1.2.2 AbstractAutowireCapableBeanFactory#convertForProperty

    @Nullable
    private Object convertForProperty(
            @Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {

        // 判断是否是BeanWrapperImpl类型
        // BeanWrapperImpl实现了TypeConverterSupport接口,所以也支持类型转换
        if (converter instanceof BeanWrapperImpl) {
            return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
        }
        else {
            PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
            // 获取set方法
            MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
            return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
        }
    }

前文提到,在applyPropertyValues中调用了convertForProperty进行类型转换,可以看到,这个方法的逻辑如下:

  • 判断入参converter是否是BeanWrapperImpl类型,如果是,交由BeanWrapperImpl进行转换。之所以需要判断是否是BeanWrapperImpl类型,是因为BeanWrapperImpl实现了TypeConverterSupport接口,所以它也是支持类型转换的
  • 如果不是BeanWrapperImpl类型,那么交由converterconvertIfNecessary进行转换

converter有以下三个实现类:

convertIfNecessary具体实现类

可以看到

  • CustomTypeConverter:测试包下的,明显不是我们关心的实现
  • DataBinder:数据绑定器,应该是spring mvc入参做转换的实现类,也不是本篇的关心内容
  • TypeConverterSupport:类型转换支持,通过debug可以确定是进入该类的实现

1.2.3 TypeConverterSupport#convertIfNecessary

    @Override
    @Nullable
    public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
            @Nullable MethodParameter methodParam) throws TypeMismatchException {

        return convertIfNecessary(value, requiredType,
                (methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
    }

    @Nullable
    @Override
    public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
            @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {

        Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
        try {
            // 委托给typeConverterDelegate进行类型转换
            return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
        }
        catch (ConverterNotFoundException | IllegalStateException ex) {
            throw new ConversionNotSupportedException(value, requiredType, ex);
        }
        catch (ConversionException | IllegalArgumentException ex) {
            throw new TypeMismatchException(value, requiredType, ex);
        }
    }

可以看到重载方法中委托给了typeConverterDelegate进行类型转换

1.2.4 TypeConverterDelegate#convertIfNecessary

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

        // No custom editor but custom ConversionService specified?
        // 获取注册的ConversionService
        // ConversionService是一个定义了canConvert以及convert方法的接口
        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;
                }
            }
        }
        // 其余省略
}
  • 首先获取到ConversionServiceConversionService是一个定义了canConvert以及convert方法的接口(该接口由GenericConversionService实现):
public interface ConversionService {
    
    boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);

    boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
    
    @Nullable
    <T> T convert(@Nullable Object source, Class<T> targetType);
    
    @Nullable
    Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

}
  • 之后通过ConversionServicecanConvert方法判断是否可以进行转换,如果可以,则通过convert方法进行转换

1.2.5 GenericConversionService#canConvert

    @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;
        }
        // 获取转化器,如果不为空,那就是可以转化的
        GenericConverter converter = getConverter(sourceType, targetType);
        return (converter != null);
    }

通过sourceTypetargetType来获取转换器converter,如果能获取到转换器(converter != null),自然说明是可以进行转换的

1.2.6 GenericConversionService#getConverter

    @Nullable
    protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
        // 构建key
        ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
        // 通过key去缓存中查找
        GenericConverter converter = this.converterCache.get(key);
        if (converter != null) {
            return (converter != NO_MATCH ? converter : null);
        }

        // 缓存查找不到,从converters中查找
        converter = this.converters.find(sourceType, targetType);
        if (converter == null) {
            // 获取获取不到,那么获取默认converter
            // 该默认converter可能是NO_OP_CONVERTER(targetType是sourceType的超类),否则为空
            converter = getDefaultConverter(sourceType, targetType);
        }

        // 如果converters查找的到,那么加入缓存
        if (converter != null) {
            this.converterCache.put(key, converter);
            return converter;
        }

        this.converterCache.put(key, NO_MATCH);
        return null;
    }
  • 首先从缓存converterCache中查找
    • 若有,直接返回
    • 若没有,通过find方法进行匹配
      • 若匹配不到,通过getDefaultConverter获取默认转换器
  • 如果转换器不为空,加入缓存converterCache当中,并返回该转换器
  • 否则,往converterCache写入没有匹配结果的缓存,并返回null

1.2.7 GenericConversionService#find

        @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());
            for (Class<?> sourceCandidate : sourceCandidates) {
                for (Class<?> targetCandidate : targetCandidates) {
                    // 构建ConvertiblePair对象
                    ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
                    // 获取已注册的converter
                    GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
                    if (converter != null) {
                        return converter;
                    }
                }
            }
            return null;
        }
  • 通过getClassHierarchy分别获取sourceTypetargetType的超类及这些超类对应的接口
  • 通过双重for循环,依次对sourceCandidatestargetCandidates构造ConvertiblePair对象
  • 通过getRegisteredConverter查找ConvertiblePair对象对应的转换器converter,不为null则直接返回

1.2.8 GenericConversionService#getClassHierarchy

        private List<Class<?>> getClassHierarchy(Class<?> type) {
            List<Class<?>> hierarchy = new ArrayList<>(20);
            Set<Class<?>> visited = new HashSet<>(20);
            // 加入type对应的类型
            addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited);
            boolean array = type.isArray();

            int i = 0;
            // 开始递归,将type对应的父类型以及接口不断加入hierarchy中知道碰到Object
            while (i < hierarchy.size()) {
                Class<?> candidate = hierarchy.get(i);
                candidate = (array ? candidate.getComponentType() : ClassUtils.resolvePrimitiveIfNecessary(candidate));
                Class<?> superclass = candidate.getSuperclass();
                // 跳出循环条件
                if (superclass != null && superclass != Object.class && superclass != Enum.class) {
                    // 加入父类型
                    addToClassHierarchy(i + 1, candidate.getSuperclass(), array, hierarchy, visited);
                }
                // 加入接口
                addInterfacesToClassHierarchy(candidate, array, hierarchy, visited);
                i++;
            }

            // 处理Enum
            if (Enum.class.isAssignableFrom(type)) {
                addToClassHierarchy(hierarchy.size(), Enum.class, array, hierarchy, visited);
                addToClassHierarchy(hierarchy.size(), Enum.class, false, hierarchy, visited);
                addInterfacesToClassHierarchy(Enum.class, array, hierarchy, visited);
            }

            // 处理Object
            addToClassHierarchy(hierarchy.size(), Object.class, array, hierarchy, visited);
            addToClassHierarchy(hierarchy.size(), Object.class, false, hierarchy, visited);
            return hierarchy;
        }

代码有点长,划分成几个部分进行解读

addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited);
  • 首先往hierarchyvisited这俩个集合压入第一个元素,该元素为type对应的class类型,resolvePrimitiveIfNecessary方法顾名知义:在有必要的情况下,将基本类型转化为包装类型:
    private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new IdentityHashMap<>(8);
    static {
        primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
        primitiveWrapperTypeMap.put(Byte.class, byte.class);
        primitiveWrapperTypeMap.put(Character.class, char.class);
        primitiveWrapperTypeMap.put(Double.class, double.class);
        primitiveWrapperTypeMap.put(Float.class, float.class);
        primitiveWrapperTypeMap.put(Integer.class, int.class);
        primitiveWrapperTypeMap.put(Long.class, long.class);
        primitiveWrapperTypeMap.put(Short.class, short.class);
        primitiveWrapperTypeMap.put(Void.class, void.class);

        // Map entry iteration is less expensive to initialize than forEach with lambdas
        for (Map.Entry<Class<?>, Class<?>> entry : primitiveWrapperTypeMap.entrySet()) {
            // key和value互换
            primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey());
            registerCommonClasses(entry.getKey());
        }
    }
    public static Class<?> resolvePrimitiveIfNecessary(Class<?> clazz) {
        Assert.notNull(clazz, "Class must not be null");
        return (clazz.isPrimitive() && clazz != void.class ? primitiveTypeToWrapperMap.get(clazz) : clazz);
    }

addToClassHierarchy方法则是将元素按照index加入到hierarchyvisited俩个集合中:

        private void addToClassHierarchy(int index, Class<?> type, boolean asArray,
                List<Class<?>> hierarchy, Set<Class<?>> visited) {

            if (asArray) {
                // 如果是数组,获取数组的元素类型componentType
                type = Array.newInstance(type, 0).getClass();
            }
            if (visited.add(type)) {
                hierarchy.add(index, type);
            }
        }
            while (i < hierarchy.size()) {
                Class<?> candidate = hierarchy.get(i);
                candidate = (array ? candidate.getComponentType() : ClassUtils.resolvePrimitiveIfNecessary(candidate));
                Class<?> superclass = candidate.getSuperclass();
                // 跳出循环条件
                if (superclass != null && superclass != Object.class && superclass != Enum.class) {
                    // 加入父类型
                    addToClassHierarchy(i + 1, candidate.getSuperclass(), array, hierarchy, visited);
                }
                // 加入接口
                addInterfacesToClassHierarchy(candidate, array, hierarchy, visited);
                i++;
            }
  • 接着开始递归,从之前加入的第一个元素开始,如果该元素是数组类型,获取其元素类型,否则,获取其本身类型;之后获取该类型对应的超类,如果超类不为空且既不是Object也不是Enum(递归终止条件),加入该类型到hierarchyvisited俩个集合,之后通过addInterfacesToClassHierarchy方法将该超类实现的接口也加入hierarchyvisited俩个集合
// 处理Enum
if(Enum.class.isAssignableFrom(type)) {
    addToClassHierarchy(hierarchy.size(), Enum.class, array, hierarchy, visited);
    addToClassHierarchy(hierarchy.size(), Enum.class, false, hierarchy, visited);
    addInterfacesToClassHierarchy(Enum.class, array, hierarchy, visited);
}
  • 对枚举这个顶级类也加入hierarchyvisited俩个集合(addToClassHierarchy内部有判重处理,所以这里连续调用俩次并不影响)
addToClassHierarchy(hierarchy.size(), Object.class, array, hierarchy, visited);
addToClassHierarchy(hierarchy.size(), Object.class, false, hierarchy, visited);
  • Object这个顶级类也加入hierarchyvisited俩个集合

1.2.9 GenericConversionService#getRegisteredConverter

        @Nullable
        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;
        }

convertersglobalConverters俩个集合中查找到对应的转换器

问题来了:这俩个集合的元素是什么时候添加进去的呢?

回过头看demo中的spring.xml

    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="studentConverter"/>
            </set>
        </property>
    </bean>

可以看到,在ConversionServiceFactoryBean中,我们为其属性converters设置了我们自定义的转换器studentConverter,而ConversionServiceFactoryBean实现了InitializingBean接口(《Spring源码解析(十一)-初始化bean》)的afterPropertiesSet方法:

    @Override
    public void afterPropertiesSet() {
        this.conversionService = createConversionService();
        ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
    }

可以看到通过registerConverters方法注册了转换器:

    public static void registerConverters(@Nullable Set<?> converters, ConverterRegistry registry) {
        if (converters != null) {
            for (Object converter : converters) {
                if (converter instanceof GenericConverter) {
                    registry.addConverter((GenericConverter) converter);
                }
                else if (converter instanceof Converter<?, ?>) {
                    registry.addConverter((Converter<?, ?>) converter);
                }
                else if (converter instanceof ConverterFactory<?, ?>) {
                    registry.addConverterFactory((ConverterFactory<?, ?>) converter);
                }
                else {
                    throw new IllegalArgumentException("Each converter object must implement one of the " +
                            "Converter, ConverterFactory, or GenericConverter interfaces");
                }
            }
        }
    }

在其实现中,又往registry添加了转换器:

    @Override
    public void addConverter(GenericConverter converter) {
        this.converters.add(converter);
        invalidateCache();
    }

查看add方法:

        public void add(GenericConverter converter) {
            Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
            if (convertibleTypes == null) {
                Assert.state(converter instanceof ConditionalConverter,
                        "Only conditional converters may return null convertible types");
                this.globalConverters.add(converter);
            }
            else {
                for (ConvertiblePair convertiblePair : convertibleTypes) {
                    ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
                    convertersForPair.add(converter);
                }
            }
        }
        private ConvertersForPair getMatchableConverters(ConvertiblePair convertiblePair) {
            return this.converters.computeIfAbsent(convertiblePair, k -> new ConvertersForPair());
        }

可以看到,往我们之前查找转换器的俩个集合convertersglobalConverters添加了元素

到这里,我们仅是将ConversionServicecanConvert解析完毕,接下来回过头看看真正完成解析功能的convert接口

1.2.10 GenericConversionService#convert

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

    @Override
    @Nullable
    public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
        Assert.notNull(targetType, "Target type to convert to cannot be null");
        // sourceType为空,限制source也必须为空,并对null进行处理
        if (sourceType == null) {
            Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
            return handleResult(null, targetType, convertNullSource(null, targetType));
        }
        // source和sourceType不匹配
        if (source != null && !sourceType.getObjectType().isInstance(source)) {
            throw new IllegalArgumentException("Source to convert from must be an instance of [" +
                    sourceType + "]; instead it was a [" + source.getClass().getName() + "]");
        }
        GenericConverter converter = getConverter(sourceType, targetType);
        if (converter != null) {
            // 进行转换
            Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
            // 处理结果
            return handleResult(sourceType, targetType, result);
        }
        // 处理没查找到转化器的结果
        return handleConverterNotFound(source, sourceType, targetType);
    }

调用重载方法之后,进行了以下几步的处理:

  • 如果sourceType为空,调用handleResult进一步判断结果并返回
  • 如果source不为空,但source不是sourceType的实例,那么抛出异常
  • 通过getConverter获取转换器,应用转换器的convert方法进行转换,对转换结果调用handleResult处理并返回
  • 对没有查找转换器的结果进行处理

1.2.11 GenericConversionService#handleResult

    @Nullable
    private Object handleResult(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType, @Nullable Object result) {
        // 如果targetType是基本类型,而结果为null,此时转化失败,抛出异常
        if (result == null) {
            assertNotPrimitiveTargetType(sourceType, targetType);
        }
        return result;
    }

    private void assertNotPrimitiveTargetType(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (targetType.isPrimitive()) {
            throw new ConversionFailedException(sourceType, targetType, null,
                    new IllegalArgumentException("A null value cannot be assigned to a primitive type"));
        }
    }

handleResult的逻辑其实就是如果转换结果为null,但是targetType是基本类型,那么抛出异常,因为基本类型是不能转换成null

1.2.12 ConversionUtils#invokeConverter

    @Nullable
    public static Object invokeConverter(GenericConverter converter, @Nullable Object source,
            TypeDescriptor sourceType, TypeDescriptor targetType) {

        try {
            return converter.convert(source, sourceType, targetType);
        }
        catch (ConversionFailedException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new ConversionFailedException(sourceType, targetType, source, ex);
        }
    }

交由converterconvert方法完成转换,对demo进行debug,通过调用栈可以发现使用的是GenericConversionService的内部类ConverterAdapterconvert方法实现:

        @Override
        @Nullable
        public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            if (source == null) {
                return convertNullSource(sourceType, targetType);
            }
            return this.converter.convert(source);
        }

而这里的convert方法具体实现,便是demo中自定义的convert方法

2 ConverterFactory

Converter实现的是一种类型到另一种类型到转换,但有些场景下,可能需要从一种类型转换成多种类型,这多种类型继承或实现自同一个父类,如果使用Converter一个个去转换,显然不可取。对于这种情况,spring提供了ConverterFactory,其定义如下:

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

}

可以看到,ConverterFactory支持从S类型转换为T类型,而T类型是R类型的子类,实现了“一对多”的类型转换。

2.1 DEMO

@Component
public class StringToEnumConvertFactory implements ConverterFactory<String, Enum> {
    @Override
    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToEnum<>(targetType);
    }

    private class StringToEnum<T extends Enum> implements Converter<String, T> {

        private Class<T> enumType;

        StringToEnum(Class<T> enumType) {
            this.enumType = enumType;
        }

        @Override
        public T convert(String source) {
            if (source.length() == 0) {
                return null;
            }
            return Enum.valueOf(enumType, source.trim());
        }
    }
}

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

}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {

}

在源类型与目标类型已经匹配的基础上再进行判断是否支持转换

3.1 DEMO

final class StringToArrayConverter implements ConditionalGenericConverter {

    private final ConversionService conversionService;


    public StringToArrayConverter(ConversionService conversionService) {
        this.conversionService = conversionService;
    }


    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(String.class, Object[].class));
    }

    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return ConversionUtils.canConvertElements(sourceType, targetType.getElementTypeDescriptor(),
                this.conversionService);
    }

    @Override
    @Nullable
    public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null) {
            return null;
        }
        String string = (String) source;
        String[] fields = StringUtils.commaDelimitedListToStringArray(string);
        TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
        Assert.state(targetElementType != null, "No target element type");
        Object target = Array.newInstance(targetElementType.getType(), fields.length);
        for (int i = 0; i < fields.length; i++) {
            String sourceElement = fields[i];
            Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetElementType);
            Array.set(target, i, targetElement);
        }
        return target;
    }

}

4 GenericConverter

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

}

GenericConverter是底层的实现,ConverterConverterFactoryConditionalConverter都是在它的基础上的封装,spring建议使用这三者来实现功能而并非直接使用GenericConverter

5 总结

本篇主要围绕spring提供的几种转换器进行了源码解析与demo演示,初步了解并熟悉了spring这一套转换体系。除了spring提供的转化器之外, com.google.common.base.Convert类也提供了相应的功能,并且支持正逆向转换:

public abstract class Converter<A, B> implements Function<A, B> {
    protected abstract B doForward(A a);
    protected abstract A doBackward(B b);
    //其他略
}

日常编码中学会使用转换器,让代码看起来简洁、易懂,是一种值得学习的编程习惯。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271