【Spring 笔记】Bean 的类型转换相关整理

  • 【Spring 笔记】Bean 的类型转换相关整理

1. 概述

  • Bean 对象里面的 属性 类型,都是通过 XML 、Properties 或者其他方式配置的。
    • Spring 容器加载过程中,这些属性都是以 String 类型加载进容器。

    • 最终再将这些 String 类型的属性转换 Bean 对象属性所对应的真正类型。

    • 这些信息以及转换过程由 Spring 类型转换体系 完成。

  • Bean 对象里面的 属性 类型,都是通过 XML 、Properties 或者其他方式配置的。
    • Spring 容器加载过程中,这些属性都是以 String 类型加载进容器。
    • 最终再将这些 String 类型的属性转换 Bean 对象属性所对应的真正类型。
    • 这些信息以及转换过程由 Spring 类型转换体系 完成。

1. 原理

  • 容器将 XML 文件中定义的 解析为 BeanDefinition(【Spring 笔记】Bean 解析相关整理)。
    • BeanDefinition 中存储着定义一个 bean 需要的所有信息,包括属性(属性以 String 类型的存储)。
    • Bean 实例化阶段时,Spring 容器会将这些属性转换为属性真正对应的类型。( 【Spring 笔记】创建 Bean 相关整理(上)、【Spring 笔记】创建 Bean 相关整理(下))
  • Bean 属性的注入是在实例化 Bean 过程的属性注入阶段完成的,主要执行了 AbstractAutowireCapableBeanFactory 类的 populateBean() 方法。
    • populateBean() 方法中将 BeanDefinition 中定义的属性值转换为 PropertyValue。
    • 再调用 applyPropertyValues() 方法,进行属性应用。
    • PropertyValue 用于保存单个 bean 属性的信息和值的对象。
// AbstractAutowireCapableBeanFactory.java#populateBean
// bean 的属性值
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
......
// 将属性应用到 bean 中
if (pvs != null) {
    
    
    applyPropertyValues(beanName, mbd, bw, pvs);
}

// AbstractAutowireCapableBeanFactory.java#applyPropertyValues
// 遍历属性,将属性转换为对应类的对应属性的类型
    for (PropertyValue pv : original) {
    
    
        // 属性值不需要转换
        if (pv.isConverted()) {
    
    
            deepCopy.add(pv);
        // 属性值需要转换
        } else {
    
    
            String propertyName = pv.getName();
            Object originalValue = pv.getValue(); // 原始的属性值,即转换之前的属性值
            Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); // 转换属性值,例如将引用转换为IoC容器中实例化对象引用 !!!!! 对属性值的解析!!
            Object convertedValue = resolvedValue; // 转换之后的属性值
            boolean convertible = bw.isWritableProperty(propertyName) &&
                    !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);  // 属性值是否可以转换
            // 使用用户自定义的类型转换器转换属性值
            if (convertible) {
    
    
                convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
            }
......
  • applyPropertyValues() 方法中的 convertForProperty() 方法会进行属性转换。
// AbstractAutowireCapableBeanFactory。java
@Nullable
private Object convertForProperty(
        @Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
    
    
    // 若 TypeConverter 为 BeanWrapperImpl 类型,则使用 BeanWrapperImpl 进行类型转换,BeanWrapperImpl 实现了 PropertyEditorRegistry 接口。
    if (converter instanceof BeanWrapperImpl) {
    
    
        return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
    } else {
    
    
        // 获得属性对应的 PropertyDescriptor 对象
        PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
        // 获得属性对应的 setting MethodParameter 对象
        MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
        // 执行转换
        return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
    }
}
  • TypeConverter 是定义类型转换方法的接口。
    • 若 TypeConverter 为 BeanWrapperImpl 类型,则使用 BeanWrapperImpl 进行类型转换。
    • 否则,调用 TypeConverter 的 convertIfNecessary() 方法,进行类型转换。
  • convertIfNecessary() 方法的实现者有两个,DataBinder 和 TypeConverterSupport 类。
    • DataBinder 主要用于参数绑定。
    • TypeConverterSupport 则是 TypeConverter 的基本实现。
// TypeConverterSupport.java
@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
        throws TypeMismatchException {
    
    
    return doConvert(value, requiredType, null, field);
}

@Nullable
private <T> T doConvert(@Nullable Object value,@Nullable Class<T> requiredType,
        @Nullable MethodParameter methodParam, @Nullable Field field) throws TypeMismatchException {
    
    
    Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
    try {
    
    
        if (field != null) {
    
     // field
            return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);
        } else {
    
     // methodParam
            return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
        }
    } catch (ConverterNotFoundException | IllegalStateException ex) {
    
    
        throw new ConversionNotSupportedException(value, requiredType, ex);
    } catch (ConversionException | IllegalArgumentException ex) {
    
    
        throw new TypeMismatchException(value, requiredType, ex);
    }
}
  • TypeConverterSupport 使用了 typeConverterDelegate 委托者。
// TypeConverterDelegate.java
@Nullable
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
        @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
    
    
        ......
        // 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;
                }
            }
        }
       ......
}
  • 如果没有自定义的编辑器则使用 ConversionService(Spring 3 后推出的用于替代 PropertyEditor
    转换模式 的转换体系)。
// ConversionService.java
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);

}

在这里插入图片描述

  • ConfigurableConversionService 用于合并 ConversionService 和
    ConverterRegistry 两个接口操作,适用于转换器的添加、删除、转换验证和转换执行操作。
  • GenericConversionService 用于实现转换器的注册和使用基础方法。
  • DefaultConversionService 为 ConversionService 接口的默认实现,适用于大部分条件下的转换工作。

convert

// ConversionService.java
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
参数 说明
source 要转换的源对象,可以为 null 。
sourceType source 的类型的上下文,如果 source 为 null ,则可以为 null 。
stargetType source 要转换的类型的上下文。
  • convert() 方法,将给定的源对象 source 转换为指定的 targetType。
  • TypeDescriptors 提供有关发生转换的源位置和目标位置的附加上下文,通常是对象字段或属性位置。
// GenericConversionService.java
@Override
@Nullable
public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
    
    
    Assert.notNull(targetType, "Target type to convert to cannot be null");
    // 1. 如果 sourceType 为空,则直接处理结果
    if (sourceType == null) {
    
    
        Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
        return handleResult(null, targetType, convertNullSource(null, targetType));
    }
    // 2. 如果类型不对,抛出 IllegalArgumentException 异常
    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() + "]");
    }
    // 3. 获得对应的 GenericConverter 对象
    GenericConverter converter = getConverter(sourceType, targetType);
    // 4. 如果 converter 非空,则进行转换,然后再处理结果
    if (converter != null) {
    
    
        // 4.1. 执行转换
        Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
        // 4.2. 处理器结果
        return handleResult(sourceType, targetType, result);
    }
    // 5. 处理 converter 为空的情况
    return handleConverterNotFound(source, sourceType, targetType);
}
  • 方法调用流程。
    • 步骤 1,如果 sourceType 为空,则直接处理结果。
    • 步骤 2,如果类型不对,抛出 IllegalArgumentException 异常。
    • 步骤 3,调用 getConverter() 方法,获取 GenericConverter 对象。
    • 步骤 4,如果 converter 非空,则先进行转换,然后再处理结果。

步骤 4.1,调用 ConversionUtils#invokeConverter() 方法,执行转换。

// ConversionUtils.java
@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);
    }
}
  • 步骤 4.2,调用 handleResult() 方法,处理结果。
// GenericConversionService.java
@Nullable
private Object handleResult(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType, @Nullable Object result) {
    
    
    if (result == null) {
    
    
        assertNotPrimitiveTargetType(sourceType, targetType);
    }
    return result;
}

private void assertNotPrimitiveTargetType(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
    
    
    if (targetType.isPrimitive()) {
    
    
        throw new ConversionFailedException(sourceType, targetType, null,
                new IllegalArgumentException("A null value cannot be assigned to a primitive type"));
    }
}
  • 步骤 5,调用 handleConverterNotFound() 方法,处理 converter 为空的情况。
// GenericConversionService.java
@Nullable
private Object handleConverterNotFound(
        @Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
    
    
    // 1. 如果 source 为空,则返回空
    if (source == null) {
    
    
        assertNotPrimitiveTargetType(sourceType, targetType);
        return null;
    }
    // 2. 如果 sourceType 为空,或者 targetType 是 sourceType 的子类,则返回 source
    if ((sourceType == null || sourceType.isAssignableTo(targetType)) &&
            targetType.getObjectType().isInstance(source)) {
    
    
        return source;
    }
    // 3. 抛出 ConverterNotFoundException 异常
    throw new ConverterNotFoundException(sourceType, targetType);
}
  • getConverter

    • 通过 getConverter() 方法,获取 GenericConverter 对象 converter。
// GenericConversionService.java
@Nullable
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
    
    
    // 创建 ConverterCacheKey 对象
    ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
    // 从 converterCache 缓存中,获得 GenericConverter 对象 converter
    GenericConverter converter = this.converterCache.get(key);
    // 如果获得到,则返回 converter
    if (converter != null) {
    
    
        return (converter != NO_MATCH ? converter : null);
    }

    // 如果获取不到,则从 converters 中查找
    converter = this.converters.find(sourceType, targetType);
    // 如果查找不到,则获得默认的 Converter 对象
    if (converter == null) {
    
    
        converter = getDefaultConverter(sourceType, targetType);
    }

    // 如果找到 converter ,则添加 converter 到 converterCache 中,并返回 converter
    if (converter != null) {
    
    
        this.converterCache.put(key, converter);
        return converter;
    }

    // 如果找不到 converter ,则添加 NO_MATCH 占位符到 converterCache 中,并返回 null
    this.converterCache.put(key, NO_MATCH);
    return null;
}
  • 从 converterCache 缓存中获取,如果存在则返回,否则从 converters 中获取,并且加入到
    converterCache 缓存中。
  • Converters 是 GenericConversionService 内部类,用于管理所有注册的转换器,其内部维护一个 Set 和
    Map 的数据结构用于管理转换器。
// GenericConversionService.java#Converters
private final Set<GenericConverter> globalConverters = new LinkedHashSet<>();

private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);
  • Converters 对象的 find() 方法,可以查找相应的 GenericConverter。
// GenericConversionService.java#Converters
@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());
    // 遍历 sourceCandidates 数组
    for (Class<?> sourceCandidate : sourceCandidates) {
    
    
        // 遍历 targetCandidates 数组
        for (Class<?> targetCandidate : targetCandidates) {
    
    
            // 创建 ConvertiblePair 对象
            ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
            // 获得 GenericConverter 对象
            GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
            if (converter != null) {
    
    
                return converter;
            }
        }
    }
    return null;
}

@Nullable
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
        TypeDescriptor targetType, ConvertiblePair convertiblePair) {
    
    
    // Check specifically registered converters
    // 从 converters 中,获得 converter
    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
    // 从 globalConverters 中,获得 globalConverter
    for (GenericConverter globalConverter : this.globalConverters) {
    
    
        if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
    
    
            return globalConverter;
        }
    }
    return null;
}
  • find() 方法中,根据 sourceType 和 targetType 查询 Converters 中维护的 Map
    是否包括支持的注册类型。
    • 如果存在返回 GenericConverter,不存在返回 null。
    • 获取 GenericConverter 后,可调用 convert() 方法,进行类型转换。

2.1 类型转换器

GenericConverter

  • GenericConverter 是一个转换接口,用于在两种或多种类型之间转换的通用型转换器接口。
    • GenericConverter 可以支持在多个源/目标类型对之间进行转换,同时也可以在类型转换过程中访问源/目标字段上下文。
    • 当更简单的 Converter 或 ConverterFactory 接口足够使用时,通常不应使用此接口。
// GenericConverter.java
public interface GenericConverter {
    
    

    @Nullable
    Set<ConvertiblePair> getConvertibleTypes();

    @Nullable
    Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}
  • GenericConverter 接口有很多的转换子类实现(如
    ArrayToArrayConverter、ArrayToCollectionConverter、ArrayToObjectConverter、ArrayToStringConverter、StringToArrayConverter
    等等)。
// StringToArrayConverter.java
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 对象
        TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
        Assert.state(targetElementType != null, "No target element type");
        // 创建目标数组
        Object target = Array.newInstance(targetElementType.getType(), fields.length);
        // 遍历 fields 数组,逐个转换
        for (int i = 0; i < fields.length; i++) {
    
    
            String sourceElement = fields[i];
            // 执行转换
            Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetElementType);
            // 设置到 target 中
            Array.set(target, i, targetElement);
        }
        return target;
    }

}
  • 类型转换体系中,Spring 提供了非常多的类型转换器,除了 GenericConverter,还提供有
    Converter、ConditionalConverter、ConverterFactory。

Converter

  • Converter 是一个将 S 类型 的源对象转换为 T 类型 的目标对象的转换器。

  • 该接口是线程安全的,可以共享。

// Converter.java
public interface Converter<S, T> {
    
    

    @Nullable
    T convert(S source);

}
  • 同样有着很多的转换子类实现。
public static void registerConverters(ConverterRegistry registry) {
    
    
    DateFormatterRegistrar.addDateConverters(registry);
    registry.addConverter(new DateTimeConverters.LocalDateTimeToLocalDateConverter());
    registry.addConverter(new DateTimeConverters.LocalDateTimeToLocalTimeConverter());
    registry.addConverter(new DateTimeConverters.ZonedDateTimeToLocalDateConverter());
    registry.addConverter(new DateTimeConverters.ZonedDateTimeToLocalTimeConverter());
    registry.addConverter(new DateTimeConverters.ZonedDateTimeToLocalDateTimeConverter());
    registry.addConverter(new DateTimeConverters.ZonedDateTimeToOffsetDateTimeConverter());
    registry.addConverter(new DateTimeConverters.ZonedDateTimeToInstantConverter());
    registry.addConverter(new DateTimeConverters.OffsetDateTimeToLocalDateConverter());
    registry.addConverter(new DateTimeConverters.OffsetDateTimeToLocalTimeConverter());
    registry.addConverter(new DateTimeConverters.OffsetDateTimeToLocalDateTimeConverter());
    registry.addConverter(new DateTimeConverters.OffsetDateTimeToZonedDateTimeConverter());
    registry.addConverter(new DateTimeConverters.OffsetDateTimeToInstantConverter());
    registry.addConverter(new DateTimeConverters.CalendarToZonedDateTimeConverter());
    registry.addConverter(new DateTimeConverters.CalendarToOffsetDateTimeConverter());
    registry.addConverter(new DateTimeConverters.CalendarToLocalDateConverter());
    registry.addConverter(new DateTimeConverters.CalendarToLocalTimeConverter());
    registry.addConverter(new DateTimeConverters.CalendarToLocalDateTimeConverter());
    registry.addConverter(new DateTimeConverters.CalendarToInstantConverter());
    registry.addConverter(new DateTimeConverters.LongToInstantConverter());
    registry.addConverter(new DateTimeConverters.InstantToLongConverter());
}

ConditionalConverter

  • ConditionalConverter 接口用于有条件的类型转换,通过转入的 sourceType 与 targetType
    判断转换能否匹配,只有可匹配的转换才可以调用 convert() 方法进行转换。
// ConditionalConverter.java
public interface ConditionalConverter {
    
    

    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);

}

ConverterFactory

  • 一个转换工厂,可以将对象从 S 转换为 R 的子类型。
// ConverterFactory.java
public interface ConverterFactory<S, R> {
    
    

    <T extends R> Converter<S, T> getConverter(Class<T> targetType);

}
  • 四种不同的转换器承载着不同的转换过程

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

2.2 GenericConversionService

  • ConversionService 接口中定义了两类方法,这两类方法都是在 GenericConversionService 中实现。

    • canConvert() 方法,用于判 sourceType 能否转成 targetType 。
    • convert() 方法,用于将 source 转成转入的 TargetType 类型实例。
    • GenericConversionService 类实现了
  • ConfigurableConversionService 接口,即支持了不同类型之间的转换,也可以对各类型转换器进行管理。

// GenericConversionService.java
@Override
public void addConverter(Converter<?, ?> converter) {
    
    
    // 1. 获取 ResolvableType 对象,基于 converter.getClass() 类
    ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
    // 1. 如果获取不到,并且 converter 是 DecoratingProxy 类型,则基于 ((DecoratingProxy) converter).getDecoratedClass() 类
    if (typeInfo == null && converter instanceof DecoratingProxy) {
    
    
        typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);
    }
    // 如果获取不到,抛出 IllegalArgumentException 异常
    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?");
    }
    // 2. 封装成 ConverterAdapter 对象,添加到 converters 中
    addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
}
  • 方法调用流程。
    • 步骤 1,根据 converter 获取 ResolvableType 数组。
    • 步骤 2,将其与 converter 封装成一个 ConverterAdapter 实
      *例,调用 addConverter() 方法,添加到 converters 中。
      ResolvableType 用于封装 Java 的 Type 类型。
    • ConverterAdapter 是 Converter 的一个适配器, 它实现了 GenericConverter和 ConditionalConverter 两个类型转换器。

addConverter

// GenericConversionService.java
@Override
public void addConverter(GenericConverter converter) {
    
    
    // 添加到 converters 中
    this.converters.add(converter);
    // 过期缓存
    invalidateCache();
}
  • 内部类 Converters 的 add() 方法。
// GenericConversionService.java
public void add(GenericConverter converter) {
    
    
    // 1. 获得 ConvertiblePair 集合
    Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
    // 2. 如果为空,并且 converter 是 ConditionalConverter 类型,则添加到 globalConverters 中
    if (convertibleTypes == null) {
    
    
        Assert.state(converter instanceof ConditionalConverter,
                "Only conditional converters may return null convertible types");
        this.globalConverters.add(converter);
    } else {
    
    
        // 3. 通过迭代的方式依次添加 converters 中
        for (ConvertiblePair convertiblePair : convertibleTypes) {
    
    
            // 从 converters 中,获得 ConvertersForPair 对象
            ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
            // 添加 converter 到 ConvertersForPair 中
            convertersForPair.add(converter);
        }
    }
}
  • 方法调用流程。

    • 步骤 1,调用 GenericConverter 的 getConvertibleTypes() 方法,获取 ConvertiblePair 集合。
    • 步骤 2,如果为空,则加入到 globalConverters 集合中。
    • 步骤 3,否则通过迭代的方式依次添加 converters 中。
  • ConvertiblePair 为 source-to-target 的持有者,它持有 source 和 target 的 class 类型。

// GenericConverter.java#ConvertiblePair
final class ConvertiblePair {
    
    

    private final Class<?> sourceType;
    private final Class<?> targetType;
    ......
}
  • 在迭代过程中会根据 ConvertiblePair 获取相应的 ConvertersForPair 对象,然后添加 converter
    转换器。
// GenericConversionService.java#ConvertersForPair
private static class ConvertersForPair {
    
    

   private final LinkedList<GenericConverter> converters = new LinkedList<>();

    public void add(GenericConverter converter) {
    
    
        this.converters.addFirst(converter);
    }

    @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.3 DefaultConversionService

  • DefaultConversionService 是 ConversionService 的默认实现,为转换体系提供一些默认的转换器。
  • DefaultConversionService 构造方法中会添加默认的 Converter。
// DefaultConversionService.java
public DefaultConversionService() {
    
    
    addDefaultConverters(this);
}

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));
}
  • 同时也提供如 addCollectionConverters() 方法,用于注册其他类型的转换器。

2.4 自定义类型转换器

  • 自定义类型转换器的步骤。
    • 实现 Converter、GenericConverter 或者 ConverterFactory 接口。
    • 将该类注册到 ConversionServiceFactoryBean 中。
  • ConversionServiceFactoryBean 实现了 InitializingBean 接口的 afterPropertiesSet() 方法。
  • 在 Bean 实例化 Bean 阶段,Spring 容器会检查当前 Bean 是否实现了 InitializingBean 接口,如果是则执行相应的初始化方法(【Spring 笔记】InitializingBean 和 init-method 相关整理)。
// ConversionServiceFactoryBean.java
@Override
public void afterPropertiesSet() {
    
    
    // 1. 创建 DefaultConversionService 对象
    this.conversionService = createConversionService();
    // 2. 注册到 ConversionServiceFactory 中
    ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
  • 方法调用流程。
  • 步骤 1,初始化 conversionService。
  • 步骤 2,将定义的 converters 注入到类型转换体系中。
// ConverterRegistry.java
public static void registerConverters(@Nullable Set<?> converters, ConverterRegistry registry) {
    
    
    if (converters != null) {
    
    
        // 遍历 converters 数组,逐个注册
        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");
            }
        }
    }
}
  • ConverterRegistry 是一个 Converter 注册器,定义了一系列注册方法。
  • addConverter() 方法将转换器注册到容器中。
  • 使用 Spring 容器的时候,Spring 将会自动识别出 IOC 容器中注册的 ConversionService 并且在 Bean 属性注入阶段使用自定义的转换器完成属性的转换。
    配置
<?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="TestConversionService"/>
            </set>
        </property>
    </bean>
    <bean id="TestConversionService" class="TestConversionService"/>
    <bean id="test" class="Test">
        <property name="testObj" value="18#testObj"/>
    </bean>
</beans>

用例

//TestObj.java
public class TestObj {
    
    

    private String name;
    private int age;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }
}

//TestConversionService.java
public class TestConversionService implements Converter<String, TestObj> {
    
    
    @Override
    public TestObj convert(String s) {
    
    
        if (StringUtils.hasLength(s)) {
    
    
            String[] sources = s.split("#");
            TestObj testObj = new TestObj();
            testObj.setAge(Integer.parseInt(sources[0]));
            testObj.setName(sources[1]);
            return testObj;
        }
        return null;
    }
}

//Test.java
public class Test {
    
    

    private TestObj testObj;

    public TestObj getTestObj() {
    
    
        return testObj;
    }

    public void setTestObj(TestObj testObj) {
    
    
        this.testObj = testObj;
    }

    public static void main(String[] args) {
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Test test = (Test) context.getBean("test");
        System.out.println("name:" + test.getTestObj().getName() + ",age:" + test.getTestObj().getAge());
    }
}

/* print
name:testObj,age:18
*/

猜你喜欢

转载自blog.csdn.net/qq_43078445/article/details/105185939