ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[spring-core]类型转换机制

2022-01-14 22:02:20  阅读:228  来源: 互联网

标签:类型转换 core convert Converter targetType spring 接口 class public


类型转换本质上来说是这么一个过程:
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 {
}

标签:类型转换,core,convert,Converter,targetType,spring,接口,class,public
来源: https://www.cnblogs.com/Xianhuii/p/15805593.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有