美文网首页
Spring源码解析(十九)-转换体系

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

作者: 秋水畏寒 | 来源:发表于2020-06-26 15:57 被阅读0次

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

    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);
        //其他略
    }
    

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

    相关文章

      网友评论

          本文标题:Spring源码解析(十九)-转换体系

          本文链接:https://www.haomeiwen.com/subject/qkvdtktx.html