6.盘点springmvc的常用接口之Converter(中篇)###
上一章简单介绍了Converter
接口的使用,Converter
接口是用于明确原类型和目标类型之间的转换。
那么怎么才能从原类型转换到某一类的目标类型呢?比如字符串转枚举类型,我有PersonType和PersonStatus两个枚举类型,那么就得有两个转换器PersonTypeConverter
、PersonStatusConverter
,枚举类型再多点,Converter
也跟着多。所以Spring提供了一个工厂接口org.springframework.core.convert.converter.ConverterFactory
。
接口说明
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
其中泛型S是source原类型,R是目标类型, T是R的子类型。
和一般的工厂模式一样,这就是提供给我们一个根据子类型返回相应转换器的工具。
如果按Converter
的思路做,代码应该是这样子:
public class StringToPersonStatusConverter implements Converter<String, PersonStatus> {
@Override
public PersonStatus convert(String source) {
if (Objects.isNull(source)) {
return null;
}
return PersonStatus.valueOf(source);
}
}
public class StringToPersonTypeConverter implements Converter<String, PersonType> {
@Override
public PersonType convert(String source) {
if (Objects.isNull(source)) {
return null;
}
return PersonType.valueOf(source);
}
}
每个枚举都要写转换器也太麻烦了,我们可以使用ConverterFactory
抽象出转换过程。
package org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
@SuppressWarnings({"unchecked", "rawtypes"})
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
@Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
Class<?> enumType = targetType;
while (enumType != null && !enumType.isEnum()) {
enumType = enumType.getSuperclass();
}
if (enumType == null) {
throw new IllegalArgumentException(
"The target type " + targetType.getName() + " does not refer to an enum");
}
return new StringToEnum(enumType);
}
private class StringToEnum<T extends Enum> implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
@Override
public T convert(String source) {
if (source.length() == 0) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
这就是Spring内置的StringToEnumConverterFactory
源码。
下面这个org.springframework.core.convert.converter.GenericConverter
接口是所有的Converter
接口中最灵活也是最复杂的一个类型转换接口。之前介绍的Converter
接口只支持从一个原类型转换为一个目标类型;ConverterFactory
接口只支持从一个原类型转换为一个目标类型对应的子类型;而GenericConverter
接口支持在多个不同的原类型和目标类型之间进行转换。
接口说明
public interface GenericConverter {
//返回这个GenericConverter能够转换的原类型和目标类型的组合
Set<ConvertiblePair> getConvertibleTypes();
//用于进行类型转换
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
其中ConvertiblePair
是一对原类型和目标类型的封装。
假设我们有个需求是用person的id或fullname转换成对应的Person对象,可以通过下面的
GenericConverter
实现。
package com.demo.mvc.component;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import com.demo.domain.Person;
import com.demo.service.PersonService;
public class PersonGenericConverter implements GenericConverter {
@Autowired
private PersonService personService;
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
// 构建原类型和目标类型对
Set<ConvertiblePair> pairs = new HashSet<ConvertiblePair>();
pairs.add(new ConvertiblePair(Integer.class, Person.class));
pairs.add(new ConvertiblePair(String.class, Person.class));
return pairs;
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Person person = null;
if (sourceType.getType() == Integer.class) {
// 根据id查询person
person = personService.findPersonById((Integer) source);
} else if (sourceType.getType() == String.class) {
// 根据fullname查询person
person = personService.findPersonByFullname((String) source);
}
return person;
}
}
在getConvertibleTypes
方法中添加了两组转换的组合,Integer到Person和String到Person。然后我们给PersonGenericConverter注入了一个PersonService,在convert
方法根据参数的类型来决定查询的方法。
GenericConverter
还有一个子类接口org.springframework.core.convert.converter.ConditionalGenericConverter
它另外继承了ConditionalConverter
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
顾名思义就是多了matches
方法用于条件判断,使得转换器不仅在满足类型匹配时可以转换,还要满足此条件。看一下Spring内置的StringToArrayConverter
就好理解了,它实现了ConditionalGenericConverter
。在字符串转换到数组的过程中,不仅要原类型为String,目标类型是数组,还有目标类型数组的元素类型也要匹配。
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 this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Object target = Array.newInstance(targetType.getElementTypeDescriptor().getType(), fields.length);
for (int i = 0; i < fields.length; i++) {
String sourceElement = fields[i];
Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor());
Array.set(target, i, targetElement);
}
return target;
}
}
友情链接