寒窗苦读十余载,奋笔疾书若干年
Spring版本
5.2.5.RELEASE
参考
源码解读
在之前的文章《Spring源码解析(十)-填充bean属性》中的applyPropertyValues
方法,使用到了convertForProperty
做了类型转换,本篇,就让我们深入了解一下spring的转换体系。
spring中存在以下几种转换器,他们分别支持不同的转换功能:
-
Converter
:用于 1:1 的 source -> target 类型转换。 -
ConverterFactory
:用于 1:N 的 source -> target 类型转换。 -
ConditionalConverter
:有条件的 source -> target 类型转换。 -
GenericConverter
:用于 N:N 的 source -> target 类型转换。
1 Converter
1.1 Demo
在查看源码之前,先从一个例子入手,方便我们debug跟踪源码逻辑
1.1.1 Student
public class Student {
private String id;
private String name;
private String desc;
private StudentService studentService;
static class StudentService{
private Integer age;
private String name;
// 省略getters和setters
}
// 省略getters和setters
}
1.1.2 StudentConverter
public class StudentConverter implements Converter<String, StudentService> {
@Override
public StudentService convert(String source) {
if (StringUtils.hasLength(source)) {
String[] sources = StringUtils.split(source, "#");
if (sources != null && sources.length >= 2) {
StudentService student = new StudentService();
student.setAge(Integer.valueOf(sources[0]));
student.setName(sources[1]);
return student;
}
}
return null;
}
}
1.1.3 spring.xml
<?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="studentConverter"/>
</set>
</property>
</bean>
<bean id="studentConverter" class="com.kungyu.custom.element.StudentConverter"/>
<bean id="student" class="com.kungyu.custom.element.Student">
<property name="studentService" value="18#123"/>
</bean>
</beans>
1.1.4 测试
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getStudentService().getAge());
System.out.println(student.getStudentService().getName());
}
结果:
18
123
逻辑比较简单,在spring.xml
中,我们注册了一个StudentConverter
类型的转换器bean,之后将这个转换器bean添加到ConversionServiceFactoryBean
的转换器列表converters
中。然后声明了一个Student
类型的bean,该bean的studentService
属性是一个内部类,不过该属性初始参数传入了string
类型的18#123
,最后通过转换器成功将18#123
转换为StudentService
的age
属性和name
属性,实现了string
到StudentService
的转换。
1.2 源码
1.2.1 Converter
@FunctionalInterface
public interface Converter<S, T> {
/**
* Convert the source object of type {@code S} to target type {@code T}.
* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
* @return the converted object, which must be an instance of {@code T} (potentially {@code null})
* @throws IllegalArgumentException if the source cannot be converted to the desired target type
*/
@Nullable
T convert(S source);
}
可以看到converter
的源码很简单,是一个函数式接口,定义了convert
函数,由子类去具体实现
1.2.2 AbstractAutowireCapableBeanFactory#convertForProperty
@Nullable
private Object convertForProperty(
@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
// 判断是否是BeanWrapperImpl类型
// BeanWrapperImpl实现了TypeConverterSupport接口,所以也支持类型转换
if (converter instanceof BeanWrapperImpl) {
return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
}
else {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// 获取set方法
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
}
}
前文提到,在applyPropertyValues
中调用了convertForProperty
进行类型转换,可以看到,这个方法的逻辑如下:
- 判断入参
converter
是否是BeanWrapperImpl
类型,如果是,交由BeanWrapperImpl
进行转换。之所以需要判断是否是BeanWrapperImpl
类型,是因为BeanWrapperImpl
实现了TypeConverterSupport
接口,所以它也是支持类型转换的 - 如果不是
BeanWrapperImpl
类型,那么交由converter
的convertIfNecessary
进行转换
converter
有以下三个实现类:
可以看到
-
CustomTypeConverter
:测试包下的,明显不是我们关心的实现 -
DataBinder
:数据绑定器,应该是spring mvc
入参做转换的实现类,也不是本篇的关心内容 -
TypeConverterSupport
:类型转换支持,通过debug可以确定是进入该类的实现
1.2.3 TypeConverterSupport#convertIfNecessary
@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException {
return convertIfNecessary(value, requiredType,
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
}
@Nullable
@Override
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
// 委托给typeConverterDelegate进行类型转换
return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
}
catch (ConverterNotFoundException | IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (ConversionException | IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
可以看到重载方法中委托给了typeConverterDelegate
进行类型转换
1.2.4 TypeConverterDelegate#convertIfNecessary
@Nullable
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// No custom editor but custom ConversionService specified?
// 获取注册的ConversionService
// ConversionService是一个定义了canConvert以及convert方法的接口
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
,ConversionService
是一个定义了canConvert
以及convert
方法的接口(该接口由GenericConversionService
实现):
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);
}
- 之后通过
ConversionService
的canConvert
方法判断是否可以进行转换,如果可以,则通过convert
方法进行转换
1.2.5 GenericConversionService#canConvert
@Override
public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
if (sourceType == null) {
return true;
}
// 获取转化器,如果不为空,那就是可以转化的
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
通过sourceType
和targetType
来获取转换器converter
,如果能获取到转换器(converter != null
),自然说明是可以进行转换的
1.2.6 GenericConversionService#getConverter
@Nullable
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
// 构建key
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
// 通过key去缓存中查找
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
// 缓存查找不到,从converters中查找
converter = this.converters.find(sourceType, targetType);
if (converter == null) {
// 获取获取不到,那么获取默认converter
// 该默认converter可能是NO_OP_CONVERTER(targetType是sourceType的超类),否则为空
converter = getDefaultConverter(sourceType, targetType);
}
// 如果converters查找的到,那么加入缓存
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
this.converterCache.put(key, NO_MATCH);
return null;
}
- 首先从缓存
converterCache
中查找- 若有,直接返回
- 若没有,通过
find
方法进行匹配- 若匹配不到,通过
getDefaultConverter
获取默认转换器
- 若匹配不到,通过
- 如果转换器不为空,加入缓存
converterCache
当中,并返回该转换器 - 否则,往
converterCache
写入没有匹配结果的缓存,并返回null
1.2.7 GenericConversionService#find
@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());
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
// 构建ConvertiblePair对象
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
// 获取已注册的converter
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
- 通过
getClassHierarchy
分别获取sourceType
和targetType
的超类及这些超类对应的接口 - 通过双重for循环,依次对
sourceCandidates
和targetCandidates
构造ConvertiblePair
对象 - 通过
getRegisteredConverter
查找ConvertiblePair
对象对应的转换器converter
,不为null
则直接返回
1.2.8 GenericConversionService#getClassHierarchy
private List<Class<?>> getClassHierarchy(Class<?> type) {
List<Class<?>> hierarchy = new ArrayList<>(20);
Set<Class<?>> visited = new HashSet<>(20);
// 加入type对应的类型
addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited);
boolean array = type.isArray();
int i = 0;
// 开始递归,将type对应的父类型以及接口不断加入hierarchy中知道碰到Object
while (i < hierarchy.size()) {
Class<?> candidate = hierarchy.get(i);
candidate = (array ? candidate.getComponentType() : ClassUtils.resolvePrimitiveIfNecessary(candidate));
Class<?> superclass = candidate.getSuperclass();
// 跳出循环条件
if (superclass != null && superclass != Object.class && superclass != Enum.class) {
// 加入父类型
addToClassHierarchy(i + 1, candidate.getSuperclass(), array, hierarchy, visited);
}
// 加入接口
addInterfacesToClassHierarchy(candidate, array, hierarchy, visited);
i++;
}
// 处理Enum
if (Enum.class.isAssignableFrom(type)) {
addToClassHierarchy(hierarchy.size(), Enum.class, array, hierarchy, visited);
addToClassHierarchy(hierarchy.size(), Enum.class, false, hierarchy, visited);
addInterfacesToClassHierarchy(Enum.class, array, hierarchy, visited);
}
// 处理Object
addToClassHierarchy(hierarchy.size(), Object.class, array, hierarchy, visited);
addToClassHierarchy(hierarchy.size(), Object.class, false, hierarchy, visited);
return hierarchy;
}
代码有点长,划分成几个部分进行解读
addToClassHierarchy(0, ClassUtils.resolvePrimitiveIfNecessary(type), false, hierarchy, visited);
- 首先往
hierarchy
和visited
这俩个集合压入第一个元素,该元素为type
对应的class
类型,resolvePrimitiveIfNecessary
方法顾名知义:在有必要的情况下,将基本类型转化为包装类型:
private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new IdentityHashMap<>(8);
static {
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
primitiveWrapperTypeMap.put(Byte.class, byte.class);
primitiveWrapperTypeMap.put(Character.class, char.class);
primitiveWrapperTypeMap.put(Double.class, double.class);
primitiveWrapperTypeMap.put(Float.class, float.class);
primitiveWrapperTypeMap.put(Integer.class, int.class);
primitiveWrapperTypeMap.put(Long.class, long.class);
primitiveWrapperTypeMap.put(Short.class, short.class);
primitiveWrapperTypeMap.put(Void.class, void.class);
// Map entry iteration is less expensive to initialize than forEach with lambdas
for (Map.Entry<Class<?>, Class<?>> entry : primitiveWrapperTypeMap.entrySet()) {
// key和value互换
primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey());
registerCommonClasses(entry.getKey());
}
}
public static Class<?> resolvePrimitiveIfNecessary(Class<?> clazz) {
Assert.notNull(clazz, "Class must not be null");
return (clazz.isPrimitive() && clazz != void.class ? primitiveTypeToWrapperMap.get(clazz) : clazz);
}
addToClassHierarchy
方法则是将元素按照index
加入到hierarchy
和visited
俩个集合中:
private void addToClassHierarchy(int index, Class<?> type, boolean asArray,
List<Class<?>> hierarchy, Set<Class<?>> visited) {
if (asArray) {
// 如果是数组,获取数组的元素类型componentType
type = Array.newInstance(type, 0).getClass();
}
if (visited.add(type)) {
hierarchy.add(index, type);
}
}
while (i < hierarchy.size()) {
Class<?> candidate = hierarchy.get(i);
candidate = (array ? candidate.getComponentType() : ClassUtils.resolvePrimitiveIfNecessary(candidate));
Class<?> superclass = candidate.getSuperclass();
// 跳出循环条件
if (superclass != null && superclass != Object.class && superclass != Enum.class) {
// 加入父类型
addToClassHierarchy(i + 1, candidate.getSuperclass(), array, hierarchy, visited);
}
// 加入接口
addInterfacesToClassHierarchy(candidate, array, hierarchy, visited);
i++;
}
- 接着开始递归,从之前加入的第一个元素开始,如果该元素是数组类型,获取其元素类型,否则,获取其本身类型;之后获取该类型对应的超类,如果超类不为空且既不是
Object
也不是Enum
(递归终止条件),加入该类型到hierarchy
和visited
俩个集合,之后通过addInterfacesToClassHierarchy
方法将该超类实现的接口也加入hierarchy
和visited
俩个集合
// 处理Enum
if(Enum.class.isAssignableFrom(type)) {
addToClassHierarchy(hierarchy.size(), Enum.class, array, hierarchy, visited);
addToClassHierarchy(hierarchy.size(), Enum.class, false, hierarchy, visited);
addInterfacesToClassHierarchy(Enum.class, array, hierarchy, visited);
}
- 对枚举这个顶级类也加入
hierarchy
和visited
俩个集合(addToClassHierarchy
内部有判重处理,所以这里连续调用俩次并不影响)
addToClassHierarchy(hierarchy.size(), Object.class, array, hierarchy, visited);
addToClassHierarchy(hierarchy.size(), Object.class, false, hierarchy, visited);
- 对
Object
这个顶级类也加入hierarchy
和visited
俩个集合
1.2.9 GenericConversionService#getRegisteredConverter
@Nullable
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters
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
for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
return globalConverter;
}
}
return null;
}
从converters
和globalConverters
俩个集合中查找到对应的转换器
问题来了:这俩个集合的元素是什么时候添加进去的呢?
回过头看demo中的spring.xml
:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="studentConverter"/>
</set>
</property>
</bean>
可以看到,在ConversionServiceFactoryBean
中,我们为其属性converters
设置了我们自定义的转换器studentConverter
,而ConversionServiceFactoryBean
实现了InitializingBean
接口(《Spring源码解析(十一)-初始化bean》)的afterPropertiesSet
方法:
@Override
public void afterPropertiesSet() {
this.conversionService = createConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
可以看到通过registerConverters
方法注册了转换器:
public static void registerConverters(@Nullable Set<?> converters, ConverterRegistry registry) {
if (converters != null) {
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");
}
}
}
}
在其实现中,又往registry
添加了转换器:
@Override
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
invalidateCache();
}
查看add
方法:
public void add(GenericConverter converter) {
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
if (convertibleTypes == null) {
Assert.state(converter instanceof ConditionalConverter,
"Only conditional converters may return null convertible types");
this.globalConverters.add(converter);
}
else {
for (ConvertiblePair convertiblePair : convertibleTypes) {
ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
convertersForPair.add(converter);
}
}
}
private ConvertersForPair getMatchableConverters(ConvertiblePair convertiblePair) {
return this.converters.computeIfAbsent(convertiblePair, k -> new ConvertersForPair());
}
可以看到,往我们之前查找转换器的俩个集合converters
和globalConverters
添加了元素
到这里,我们仅是将
ConversionService
的canConvert
解析完毕,接下来回过头看看真正完成解析功能的convert
接口
1.2.10 GenericConversionService#convert
public <T> T convert(@Nullable Object source, Class<T> targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
}
@Override
@Nullable
public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
// sourceType为空,限制source也必须为空,并对null进行处理
if (sourceType == null) {
Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
return handleResult(null, targetType, convertNullSource(null, targetType));
}
// source和sourceType不匹配
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() + "]");
}
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
// 进行转换
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
// 处理结果
return handleResult(sourceType, targetType, result);
}
// 处理没查找到转化器的结果
return handleConverterNotFound(source, sourceType, targetType);
}
调用重载方法之后,进行了以下几步的处理:
- 如果
sourceType
为空,调用handleResult
进一步判断结果并返回 - 如果
source
不为空,但source
不是sourceType
的实例,那么抛出异常 - 通过
getConverter
获取转换器,应用转换器的convert
方法进行转换,对转换结果调用handleResult
处理并返回 - 对没有查找转换器的结果进行处理
1.2.11 GenericConversionService#handleResult
@Nullable
private Object handleResult(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType, @Nullable Object result) {
// 如果targetType是基本类型,而结果为null,此时转化失败,抛出异常
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"));
}
}
handleResult
的逻辑其实就是如果转换结果为null
,但是targetType
是基本类型,那么抛出异常,因为基本类型是不能转换成null
的
1.2.12 ConversionUtils#invokeConverter
@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);
}
}
交由converter
的convert
方法完成转换,对demo进行debug,通过调用栈可以发现使用的是GenericConversionService
的内部类ConverterAdapter
的convert
方法实现:
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converter.convert(source);
}
而这里的convert
方法具体实现,便是demo中自定义的convert
方法
2 ConverterFactory
Converter
实现的是一种类型到另一种类型到转换,但有些场景下,可能需要从一种类型转换成多种类型,这多种类型继承或实现自同一个父类,如果使用Converter
一个个去转换,显然不可取。对于这种情况,spring提供了ConverterFactory
,其定义如下:
public interface ConverterFactory<S, R> {
/**
* Get the converter to convert from S to target type T, where T is also an instance of R.
* @param <T> the target type
* @param targetType the target type to convert to
* @return a converter from S to T
*/
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
可以看到,ConverterFactory
支持从S类型转换为T类型,而T类型是R类型的子类,实现了“一对多”的类型转换。
2.1 DEMO
@Component
public class StringToEnumConvertFactory implements ConverterFactory<String, Enum> {
@Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnum<>(targetType);
}
private class StringToEnum<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
@Override
public T convert(String source) {
if (source.length() == 0) {
return null;
}
return Enum.valueOf(enumType, source.trim());
}
}
}
3 ConditionalConverter
public interface ConditionalConverter {
/**
* Should the conversion from {@code sourceType} to {@code targetType} currently under
* consideration be selected?
* @param sourceType the type descriptor of the field we are converting from
* @param targetType the type descriptor of the field we are converting to
* @return true if conversion should be performed, false otherwise
*/
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
在源类型与目标类型已经匹配的基础上再进行判断是否支持转换
3.1 DEMO
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 targetElementType = targetType.getElementTypeDescriptor();
Assert.state(targetElementType != null, "No target element type");
Object target = Array.newInstance(targetElementType.getType(), fields.length);
for (int i = 0; i < fields.length; i++) {
String sourceElement = fields[i];
Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetElementType);
Array.set(target, i, targetElement);
}
return target;
}
}
4 GenericConverter
public interface GenericConverter {
/**
* Return the source and target types that this converter can convert between.
* <p>Each entry is a convertible source-to-target type pair.
* <p>For {@link ConditionalConverter conditional converters} this method may return
* {@code null} to indicate all source-to-target pairs should be considered.
*/
@Nullable
Set<ConvertiblePair> getConvertibleTypes();
/**
* Convert the source object to the targetType described by the {@code TypeDescriptor}.
* @param source the source object to convert (may be {@code null})
* @param sourceType the type descriptor of the field we are converting from
* @param targetType the type descriptor of the field we are converting to
* @return the converted object
*/
@Nullable
Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
/**
* Holder for a source-to-target class pair.
*/
final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
/**
* Create a new source-to-target pair.
* @param sourceType the source type
* @param targetType the target type
*/
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}
public Class<?> getSourceType() {
return this.sourceType;
}
public Class<?> getTargetType() {
return this.targetType;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || other.getClass() != ConvertiblePair.class) {
return false;
}
ConvertiblePair otherPair = (ConvertiblePair) other;
return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
}
@Override
public int hashCode() {
return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
}
@Override
public String toString() {
return (this.sourceType.getName() + " -> " + this.targetType.getName());
}
}
}
GenericConverter
是底层的实现,Converter
、ConverterFactory
和ConditionalConverter
都是在它的基础上的封装,spring建议使用这三者来实现功能而并非直接使用GenericConverter
5 总结
本篇主要围绕spring提供的几种转换器进行了源码解析与demo演示,初步了解并熟悉了spring这一套转换体系。除了spring提供的转化器之外, com.google.common.base.Convert
类也提供了相应的功能,并且支持正逆向转换:
public abstract class Converter<A, B> implements Function<A, B> {
protected abstract B doForward(A a);
protected abstract A doBackward(B b);
//其他略
}
日常编码中学会使用转换器,让代码看起来简洁、易懂,是一种值得学习的编程习惯。
网友评论