Spring ConversionService 类型转换(一)Converter

Spring ConversionService 类型转换(一)Converter

Spring 系列目录(http://www.javashuo.com/article/p-kqecupyl-bm.html)html

Spring ConversionService 类型转换系列文章:java

  1. Spring ConversionService 类型转换(一)Converter
  2. Spring ConversionService 类型转换(二)ConversionService

JDK 提供的 PropertyEditor 只能将 String 类型转为 Object,若是要将一种 Object 类型转换成另外一种 Object 类型就只能使用 Spring 提供的 ConversionService 了,这些类都位于 org.springframework.core.convert 包下。spring

1、ConversionService 使用

private ConversionService conversionService;
@Before
public void before() {
    conversionService = new DefaultConversionService();
}

@Test
public void test() {
    ConversionService conversionService = new DefaultConversionService();
    Integer value = conversionService.convert("1", Integer.class);
    Assert.assertTrue(value == 1);
}

在 DefaultConversionService 组件中已经注册了 Spring 默认的觉转换器,能够分为如下几类:ide

  • Converter 一对一转换,把 S 类型转化成 T 类型,最经常使用的转换器
  • ConverterFactory 一对 N 转换
  • GenericConverter N 对 N 转换

2、三种转换器

三种转换器

2.1 Converter(1:1)

(1) 接口学习

@FunctionalInterface
public interface Converter<S, T> {
    T convert(S source);
}

Converter接口很简单,就是把 S 类型转化成 T 类型。咱们看一下使用方法:测试

(2) 测试ui

@Test
public void converterTest() {
    // ObjectToStringConverter
    Assert.assertEquals("false", conversionService.convert(false, String.class));
    
    // StringToBooleanConverter
    Assert.assertTrue(conversionService.convert("true", Boolean.class));
}

(3) ObjectToStringConverter 分析this

ObjectToStringConverter 和 StringToBooleanConverter 都是在 DefaultConversionService 中内置的。code

final class ObjectToStringConverter implements Converter<Object, String> {
    @Override
    public String convert(Object source) {
        return source.toString();
    }
}

2.2 ConverterFactory(1:N)

(1) 接口htm

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

R 的子类均可以统一由这个 ConverterFactory 进行转换。

(2) 测试

// 测试 ConverterFactory StringToNumberConverterFactory
@Test
public void converterFactoryTest() {
    Assert.assertTrue(conversionService.convert("1.2", double.class) == 1.2d);
    Assert.assertTrue(conversionService.convert("1", int.class) == 1);
    Assert.assertTrue(conversionService.convert("0x10", byte.class) == 0x10);
}

这里用到了 StringToNumberConverterFactory 把 String 转化成了 Number 的各个子类型,代码其实很简单:

(3) StringToNumberConverterFactory 分析

final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
    @Override
    public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToNumber<>(targetType);
    }

    private static final class StringToNumber<T extends Number> implements Converter<String, T> {
        private final Class<T> targetType;
        public StringToNumber(Class<T> targetType) {
            this.targetType = targetType;
        }

        @Override
        public T convert(String source) {
            if (source.isEmpty()) {
                return null;
            }
            // String 类型转换成 Number
            return NumberUtils.parseNumber(source, this.targetType);
        }
    }
}

2.3 GenericConverter(N:N)

(1) 接口

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
public interface ConditionalConverter {
    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

GenericConverter 是 N:N 的转化,支持转化的全部类型都写在了属性 Set 内。

(2) 测试

// 测试 GenericConverter CollectionToCollectionConverter
@Test
public void genericConverterTest() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    Set<String> set = (Set<String>) conversionService.convert(list,
            TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)),
            TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(String.class)));
    // List<Integer> -> Set<String>
    Assert.assertEquals("1", set.toArray(new String[0])[0]);
}

这里用到了 CollectionToCollectionConverter

(3) CollectionToCollectionConverter 分析

final class CollectionToCollectionConverter implements ConditionalGenericConverter {
    private final ConversionService conversionService;
    public CollectionToCollectionConverter(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

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

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

    @Override
    public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null) {
            return null;
        }
        Collection<?> sourceCollection = (Collection<?>) source;

        // 集合类型
        boolean copyRequired = !targetType.getType().isInstance(source);
        // 1. targetType 集合类型没变,不用转换
        if (!copyRequired && sourceCollection.isEmpty()) {
            return source;
        }

        // 集合元素类型
        TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
        // 2. targetType 集合元素没有指定类型,即 Object,且集合类型没变
        if (elementDesc == null && !copyRequired) {
            return source;
        }

        // 建立一个空集合
        Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
                (elementDesc != null ? elementDesc.getType() : null), sourceCollection.size());

        // 3. targetType 集合元素没有指定类型,则元素不用转换类型
        if (elementDesc == null) {
            target.addAll(sourceCollection);
        // 4. conversionService 将 sourceElement 转换为 targetElement 类型
        } else {
            for (Object sourceElement : sourceCollection) {
                Object targetElement = this.conversionService.convert(sourceElement,
                        sourceType.elementTypeDescriptor(sourceElement), elementDesc);
                target.add(targetElement);
                if (sourceElement != targetElement) {
                    copyRequired = true;
                }
            }
        }
        return (copyRequired ? target : source);
    }
}

参考:

  1. 《Spring 学习记录 3 ConversionService》:http://www.javashuo.com/article/p-zhacqikj-gk.html

天天用心记录一点点。内容也许不重要,但习惯很重要!

相关文章
相关标签/搜索