Spring类型转换器相关接口和实现原理
Spring类型转换器相关接口和实现原理
- 一、核心组件
- 1. `ConversionService`(核心接口)
- 2. `Converter<S, T>`
- 3. `GenericConverter`
- 4. `ConverterFactory<S, R>`
- 二、底层实现
- 1,普通类型转换器 Converter<S, T>
- 2,类型转换工厂 ConverterFactory<S, R>
- 3,通用类型转换器 GenericConverter
源码见:mini-spring
Spring 的 类型转换(Type Conversion) 是指:在应用中将一种 Java 类型的对象转换成另一种类型对象的机制。
在实际开发中,我们经常会遇到“类型不匹配”问题,比如:
-
表单提交的字符串要转换成数字、日期、枚举等;
-
配置文件中的字符串要转换成 Boolean、List、Class;
-
Bean 属性注入时类型不一致;
-
SpEL 表达式中的值需要自动转换类型;
-
数据绑定(如 Web MVC 参数绑定)中,request 参数是字符串,要绑定到业务对象中。
Spring 提供了统一的类型转换框架来处理这些场景。
本节就Spring如何实现类型转换展开叙述,下节介绍如何将其融入生命周期当中
一、核心组件
1. ConversionService
(核心接口)
Spring 类型转换的核心服务接口,负责执行类型之间的转换:
public interface ConversionService {boolean canConvert(Class<?> sourceType, Class<?> targetType);<T> T convert(Object source, Class<T> targetType);
}
- 可以检查是否支持某种转换。
- 可以执行实际的转换。
2. Converter<S, T>
这是最基本的类型转换器接口,Spring 中的所有类型转换器都实现了该接口:
public interface ConversionService {boolean canConvert(Class<?> sourceType, Class<?> targetType);<T> T convert(Object source, Class<T> targetType);
}
S
:源类型T
:目标类型
例如,将字符串转为整数的转换器:
public class StringToIntegerConverter implements Converter<String, Integer> {public Integer convert(String source) {return Integer.valueOf(source);}
}
3. GenericConverter
更灵活但也更复杂的转换器,适合处理多个源/目标类型的场景:
public class StringToIntegerConverter implements Converter<String, Integer> {public Integer convert(String source) {return Integer.valueOf(source);}
}
-
支持多个源类型与目标类型组合。
-
使用
TypeDescriptor
解决泛型、注解等更复杂的类型问题。
4. ConverterFactory<S, R>
适用于一组相关类型转换的情况。例如:String
转为 Enum
的所有子类型:
public interface ConverterFactory<S, R> {<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
二、底层实现
在上一节中,我们简要了解了类型转换的核心组件。实际上,Spring 提供了三种不同类型的转换器接口:
- 普通类型转换器(
Converter
) - 类型转换工厂(
ConverterFactory
) - 通用类型转换器(
GenericConverter
)
下面将逐一进行讲解。
1,普通类型转换器 Converter<S, T>
public interface Converter<S,T> { T convert(S source);
}
该接口结构非常简洁,只定义了一个核心方法 convert
,用于将源类型 S
转换为目标类型 T
。使用时只需实现此接口并提供具体的转换逻辑即可。
例如,将 String
转换为 Integer
:
public class StringToIntegerConverter implements Converter<String,Integer> { @Override public Integer convert(String source) { return Integer.valueOf(source); }
}
测试一下
@Test
public void testStringToIntegerConverter(){ StringToIntegerConverter converter = new StringToIntegerConverter(); Integer integer = converter.convert("10"); Assert.assertEquals(Integer.valueOf(10),integer);
}
2,类型转换工厂 ConverterFactory<S, R>
相较于 Converter
接口,ConverterFactory
适用于更广泛的转换需求。它可以通过工厂方法,为某一源类型 S
生成多个不同目标子类型 R
的转换器,常用于统一处理一组目标类型。
public interface ConverterFactory<S, R> { /** * 根据目标类型获取转换器对象 * * @param <T> 目标类型参数,表示具体的转换后数据类型,必须是R类型或其子类型 * @param targetType 目标类型的Class对象,用于指定转换后的数据类型 * @return 返回一个Converter对象,用于将源数据类型S转换为目标类型T */ <T extends R> Converter<S,T> getConverter(Class<T> targetType);
}
以下是一个将 String
转换为多个数字类型(如 Integer
、Long
)的工厂实现:
public class StringToNumberConverterFactory implements ConverterFactory<String,Number> { /** * 根据目标类型获取转换器对象 * * @param targetType 目标类型的Class对象,用于指定转换后的数据类型 * @return 返回一个Converter对象,用于将源数据类型S转换为目标类型T */ @Override public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) { return new StringToNumber<T>(targetType); } public 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.length() == 0){ return null; } if (targetType.equals(Integer.class)){ return (T) Integer.valueOf(source); } else if (targetType.equals(Long.class)) { return (T) Long.valueOf(source); }else { throw new IllegalArgumentException( "Cannot convert String [" + source + "] to target class [" + targetType.getName() + "]"); } } }
}
测试一下
@Test
public void testStringToNumberConverterFactory(){ StringToNumberConverterFactory converterFactory = new StringToNumberConverterFactory(); Converter<String, Integer> converter = converterFactory.getConverter(Integer.class); Integer integer = converter.convert("10"); Assert.assertEquals(Integer.valueOf(10),integer);
}
3,通用类型转换器 GenericConverter
GenericConverter
是 Spring 提供的最灵活、功能最强大的转换接口,适用于更复杂的场景,包括:
- 支持多个源类型和目标类型的转换(如
String → List<Integer>
、String → Set<String>
); - 利用运行时类型信息(通过
TypeDescriptor
)进行精确判断; - 适配泛型、注解、字段等更复杂的上下文信息。
public interface GenericConverter { // 执行类型转换逻辑 Object convert(Object source, Class sourceType, Class targetType); // 获取到对应的ConvertiblePair配对关系对象 Set<ConvertiblePair> getConvertibleTypes(); /** * 用于管理“源类型”和“目标类型”的配对关系 */ public static final class ConvertiblePair{ private final Class<?> sourceType; private final Class<?> targetType; public ConvertiblePair(Class<?> sourceType, Class<?> targetType) { this.sourceType = sourceType; this.targetType = targetType; } public Class<?> getSourceType() { return sourceType; } public Class<?> getTargetType() { return targetType; } @Override public boolean equals(Object object) { if (object == null || getClass() != object.getClass()) return false; ConvertiblePair that = (ConvertiblePair) object; return Objects.equals(sourceType, that.sourceType) && Objects.equals(targetType, that.targetType); } @Override public int hashCode() { return Objects.hash(sourceType, targetType); } }
}
我们可以看到在GenericConverter我们还定义了一个静态的内部类ConvertiblePair,该类是用来用于管理“源类型”和“目标类型”的配对关系。
简单来说,我们来思考一个问题当我们调用GenericConverter的convert去尝试转换类型,而GenericConverter是一个接口,具体的各种转换逻辑由子类实现,那我们又如何才能判断出,我要转换的类型是否合法,当前类型转换器当中是否支持?
所以这里就引入了ConvertiblePair用来表示一个“源类型 → 目标类型”的一组转换类型
测试一下
@Test
public void testGenericConverter(){ StringToBooleanConverter converter = new StringToBooleanConverter(); Boolean aTrue =(Boolean) converter.convert("true", String.class, Boolean.class); Assert.assertTrue(aTrue);
}