[spring-core]类型转换机制

类型转换本质上来说是这么一个过程:
SourceType --> TargetType

Spring提供了一套基于Converter接口的SPI(Server Provide Interface)机制。

通过实现Converter接口,我们可以根据自己的业务需求制定特定的类型转换规则。

1 Converter接口

org.springframework.core.convert.converter.Converter

public interface Converter<S, T> {

   @Nullable
   T convert(S source);

   default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
      Assert.notNull(after, "After Converter must not be null");
      return (S s) -> {
         T initialResult = convert(s);
         return (initialResult != null ? after.convert(initialResult) : null);
      };
   }
}

泛型解释:

  • S:原始类型。
  • T:目标类型。

方法解释:

  • convert():将数据由S类型转换到T类型。
  • andThen():链式类型转换,STU

例如,我们想要实现一个ObjectString的类型转换,可以按以下步骤进行:

  1. 实现Converter接口,指定泛型SObject,泛型TString
  2. 实现convert()方法,编写转换规则。
public class ObjectToStringConverter implements Converter<Object, String> {
    @Override
    public String convert(Object source) {
        return source.toString();
    }
}

在Spring中则可以通过以下方式调用:

  1. 创建ObjectToStringConverter,交由容器管理:
@Configuration
public class AppConfig {
    @Bean
    public Converter converter() {
        return new ObjectToStringConverter();
    }
}
  1. 在业务中注入Converter实例:
@Service
public class AppService {
    @Resource
    private Converter converter;

    public void service() {
        Date source = new Date();
        Object target = converter.convert(source);
        System.out.println(target.getClass());
        System.out.println(target);
    }
}
  1. 外界调用service()方法后,会输出以下内容,说明Date成功转换成String
class java.lang.String
Thu Jan 13 21:33:49 CST 2022

Spring的类型转换机制本质上就是这么一个套路。通过实现Converter接口,可以针对性的实现不同的类型转换规则,适用于不同的业务场景。

当然,Spring也实现了许多常用的XxxConverter,需要进行类型转换时,可以先看看Converter的继承层次,是否可以直接使用。

2 ConverterFactory接口

为了便于管理一系列具有层次关系的Converter,Spring还提供了ConverterFactory接口。

org.springframework.core.convert.converter.ConverterFactory

public interface ConverterFactory<S, R> {
   <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

泛型解释:

  • S:原始类型。
  • R:目标类型的顶层父类/接口。
  • T:特定目标类型,是R的子类/实现类。

方法解释:

  • getConverter():获取目标类型为TConverter实现类。

例如,如果有以下业务需求:

  1. CharaterInteger
  2. CharaterFloat
  3. CharaterLong
  4. ……

目标类型都是Number的子类,我们可以实现如下ConverterFactory

public class CharacterToNumberFactory implements ConverterFactory<Character, Number> {

   @Override
   public <T extends Number> Converter<Character, T> getConverter(Class<T> targetType) {
      return new CharacterToNumber<>(targetType);
   }

   private static final class CharacterToNumber<T extends Number> implements Converter<Character, T> {

      private final Class<T> targetType;

      public CharacterToNumber(Class<T> targetType) {
         this.targetType = targetType;
      }

      @Override
      public T convert(Character source) {
         return NumberUtils.convertNumberToTargetClass((short) source.charValue(), this.targetType);
      }
   }

}

在Spring中则可以通过以下方式调用:

  1. 创建CharacterToNumberFactory,交由容器管理:
@Configuration
public class AppConfig {
    @Bean
    public ConverterFactory<Character, Number> converter() {
        return new CharacterToNumberFactory();
    }
}
  1. 在业务中注入ConverterFactory实例:
@Service
public class AppService {
    @Resource
    private ConverterFactory converterFactory;

    public void service() {
        Character character = new Character('a');
        Converter integerConverter = converterFactory.getConverter(Integer.class);
        Object target = integerConverter.convert(character);
        System.out.println(target.getClass());
        System.out.println(target);
        Converter doubleConverter = converterFactory.getConverter(Double.class);
        target = doubleConverter.convert(character);
        System.out.println(target.getClass());
        System.out.println(target);
    }
}
  1. 外界调用service()方法后,会输出以下内容,说明Date成功转换成String
class java.lang.Integer
97
class java.lang.Double
97.0

所以说,本质上ConverterFactory相当于是管理一系列具有特殊层次关系的Converter的工厂,相当于是Converter的升级版本。

在需要将同一个S类型转换成多个具有层次关系的T时,可以优先考虑实现ConverterFactory

3 GenericConverter接口

org.springframework.core.convert.converter.GenericConverter接口以ConvertiblePair的形式指定sourceTypetargetType

public interface GenericConverter {

   Set<ConvertiblePair> getConvertibleTypes();

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

   final class ConvertiblePair {
      private final Class<?> sourceType;
      private final Class<?> targetType;
   }
}

GenericConverter不会在实现类中固定sourceTypetargetType的数量和类型,相比于Converter接口有了更大的灵活度,但也更加复杂。

convert()方法中,需要根据sourceTypetargetType的类型进行判断,执行对应的转换逻辑。

TypeDescriptor用来描述sourceTypetargetType的类型信息:

public class TypeDescriptor implements Serializable {
    private final Class<?> type;
    private final ResolvableType resolvableType;
    private final TypeDescriptor.AnnotatedElementAdapter annotatedElement;
}

举个例子,如果我们需要实现StringDataSize的类型转换,可以按以下例子进行:

final class StringToDataSizeConverter implements GenericConverter {
    StringToDataSizeConverter() {
    }

    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(String.class, DataSize.class));
    }

    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        return ObjectUtils.isEmpty(source) ? null : this.convert(source.toString(), this.getDataUnit(targetType));
    }

    private DataUnit getDataUnit(TypeDescriptor targetType) {
        DataSizeUnit annotation = (DataSizeUnit)targetType.getAnnotation(DataSizeUnit.class);
        return annotation != null ? annotation.value() : null;
    }

    private DataSize convert(String source, DataUnit unit) {
        return DataSize.parse(source, unit);
    }
}

4 ConditionalConverter接口

org.springframework.core.convert.converter.ConditionalConverter接口提供了matches()方法,用来判断当前Converter/GenericConverter/ConverterFactory能否完成从sourceTypetargetType的转换。

public interface ConditionalConverter {
   boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

由于ConditionalConverter接口只提供了一个预校验的方法,它更像是一个辅助性功能接口。

它更多的跟其他实际业务接口配合使用,比如上述的Converter/ConverterFactory/GenericConverter等。

5 ConditionalGenericConverter接口

org.springframework.core.convert.converter.ConditionalGenericConverter接口将ConditionConverterGenericConverter的功能整合了起来。

我们实现ConditionalGenericConverter接口就可以实现上述两个接口的功能。

public interface ConditionalGenericConverter 
    extends GenericConverter, ConditionalConverter {
}
上一篇:C#验证给定的字符串形式的日期是否合法的代码


下一篇:wget 使用三例