- 【Spring 笔记】Bean 的类型转换相关整理
1. 概述
- Bean 对象里面的 属性 类型,都是通过 XML 、Properties 或者其他方式配置的。
-
Spring 容器加载过程中,这些属性都是以 String 类型加载进容器。
-
最终再将这些 String 类型的属性转换 Bean 对象属性所对应的真正类型。
-
这些信息以及转换过程由 Spring 类型转换体系 完成。
-
- Bean 对象里面的 属性 类型,都是通过 XML 、Properties 或者其他方式配置的。
- Spring 容器加载过程中,这些属性都是以 String 类型加载进容器。
- 最终再将这些 String 类型的属性转换 Bean 对象属性所对应的真正类型。
- 这些信息以及转换过程由 Spring 类型转换体系 完成。
1. 原理
- 容器将 XML 文件中定义的 解析为 BeanDefinition(【Spring 笔记】Bean 解析相关整理)。
- BeanDefinition 中存储着定义一个 bean 需要的所有信息,包括属性(属性以 String 类型的存储)。
- Bean 实例化阶段时,Spring 容器会将这些属性转换为属性真正对应的类型。( 【Spring 笔记】创建 Bean 相关整理(上)、【Spring 笔记】创建 Bean 相关整理(下))
- Bean 属性的注入是在实例化 Bean 过程的属性注入阶段完成的,主要执行了 AbstractAutowireCapableBeanFactory 类的 populateBean() 方法。
- populateBean() 方法中将 BeanDefinition 中定义的属性值转换为 PropertyValue。
- 再调用 applyPropertyValues() 方法,进行属性应用。
- PropertyValue 用于保存单个 bean 属性的信息和值的对象。
// AbstractAutowireCapableBeanFactory.java#populateBean
// bean 的属性值
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
......
// 将属性应用到 bean 中
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
// AbstractAutowireCapableBeanFactory.java#applyPropertyValues
// 遍历属性,将属性转换为对应类的对应属性的类型
for (PropertyValue pv : original) {
// 属性值不需要转换
if (pv.isConverted()) {
deepCopy.add(pv);
// 属性值需要转换
} else {
String propertyName = pv.getName();
Object originalValue = pv.getValue(); // 原始的属性值,即转换之前的属性值
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); // 转换属性值,例如将引用转换为IoC容器中实例化对象引用 !!!!! 对属性值的解析!!
Object convertedValue = resolvedValue; // 转换之后的属性值
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); // 属性值是否可以转换
// 使用用户自定义的类型转换器转换属性值
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
......
- applyPropertyValues() 方法中的 convertForProperty() 方法会进行属性转换。
// AbstractAutowireCapableBeanFactory。java
@Nullable
private Object convertForProperty(
@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
// 若 TypeConverter 为 BeanWrapperImpl 类型,则使用 BeanWrapperImpl 进行类型转换,BeanWrapperImpl 实现了 PropertyEditorRegistry 接口。
if (converter instanceof BeanWrapperImpl) {
return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
} else {
// 获得属性对应的 PropertyDescriptor 对象
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// 获得属性对应的 setting MethodParameter 对象
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// 执行转换
return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
}
}
- TypeConverter 是定义类型转换方法的接口。
- 若 TypeConverter 为 BeanWrapperImpl 类型,则使用 BeanWrapperImpl 进行类型转换。
- 否则,调用 TypeConverter 的 convertIfNecessary() 方法,进行类型转换。
- convertIfNecessary() 方法的实现者有两个,DataBinder 和 TypeConverterSupport 类。
- DataBinder 主要用于参数绑定。
- TypeConverterSupport 则是 TypeConverter 的基本实现。
// TypeConverterSupport.java
@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
throws TypeMismatchException {
return doConvert(value, requiredType, null, field);
}
@Nullable
private <T> T doConvert(@Nullable Object value,@Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam, @Nullable Field field) throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
if (field != null) { // field
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);
} else { // methodParam
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
} catch (ConverterNotFoundException | IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
} catch (ConversionException | IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
- TypeConverterSupport 使用了 typeConverterDelegate 委托者。
// TypeConverterDelegate.java
@Nullable
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
......
// No custom editor but custom ConversionService specified?
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(Spring 3 后推出的用于替代 PropertyEditor
转换模式 的转换体系)。
// ConversionService.java
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);
}
- ConfigurableConversionService 用于合并 ConversionService 和
ConverterRegistry 两个接口操作,适用于转换器的添加、删除、转换验证和转换执行操作。 - GenericConversionService 用于实现转换器的注册和使用基础方法。
- DefaultConversionService 为 ConversionService 接口的默认实现,适用于大部分条件下的转换工作。
convert
// ConversionService.java
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
参数 | 说明 |
---|---|
source | 要转换的源对象,可以为 null 。 |
sourceType | source 的类型的上下文,如果 source 为 null ,则可以为 null 。 |
stargetType | source 要转换的类型的上下文。 |
- convert() 方法,将给定的源对象 source 转换为指定的 targetType。
- TypeDescriptors 提供有关发生转换的源位置和目标位置的附加上下文,通常是对象字段或属性位置。
// GenericConversionService.java
@Override
@Nullable
public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
// 1. 如果 sourceType 为空,则直接处理结果
if (sourceType == null) {
Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
return handleResult(null, targetType, convertNullSource(null, targetType));
}
// 2. 如果类型不对,抛出 IllegalArgumentException 异常
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() + "]");
}
// 3. 获得对应的 GenericConverter 对象
GenericConverter converter = getConverter(sourceType, targetType);
// 4. 如果 converter 非空,则进行转换,然后再处理结果
if (converter != null) {
// 4.1. 执行转换
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
// 4.2. 处理器结果
return handleResult(sourceType, targetType, result);
}
// 5. 处理 converter 为空的情况
return handleConverterNotFound(source, sourceType, targetType);
}
- 方法调用流程。
- 步骤 1,如果 sourceType 为空,则直接处理结果。
- 步骤 2,如果类型不对,抛出 IllegalArgumentException 异常。
- 步骤 3,调用 getConverter() 方法,获取 GenericConverter 对象。
- 步骤 4,如果 converter 非空,则先进行转换,然后再处理结果。
步骤 4.1,调用 ConversionUtils#invokeConverter() 方法,执行转换。
// ConversionUtils.java
@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);
}
}
- 步骤 4.2,调用 handleResult() 方法,处理结果。
// GenericConversionService.java
@Nullable
private Object handleResult(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType, @Nullable Object result) {
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"));
}
}
- 步骤 5,调用 handleConverterNotFound() 方法,处理 converter 为空的情况。
// GenericConversionService.java
@Nullable
private Object handleConverterNotFound(
@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
// 1. 如果 source 为空,则返回空
if (source == null) {
assertNotPrimitiveTargetType(sourceType, targetType);
return null;
}
// 2. 如果 sourceType 为空,或者 targetType 是 sourceType 的子类,则返回 source
if ((sourceType == null || sourceType.isAssignableTo(targetType)) &&
targetType.getObjectType().isInstance(source)) {
return source;
}
// 3. 抛出 ConverterNotFoundException 异常
throw new ConverterNotFoundException(sourceType, targetType);
}
-
getConverter
- 通过 getConverter() 方法,获取 GenericConverter 对象 converter。
// GenericConversionService.java
@Nullable
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
// 创建 ConverterCacheKey 对象
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
// 从 converterCache 缓存中,获得 GenericConverter 对象 converter
GenericConverter converter = this.converterCache.get(key);
// 如果获得到,则返回 converter
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
// 如果获取不到,则从 converters 中查找
converter = this.converters.find(sourceType, targetType);
// 如果查找不到,则获得默认的 Converter 对象
if (converter == null) {
converter = getDefaultConverter(sourceType, targetType);
}
// 如果找到 converter ,则添加 converter 到 converterCache 中,并返回 converter
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
// 如果找不到 converter ,则添加 NO_MATCH 占位符到 converterCache 中,并返回 null
this.converterCache.put(key, NO_MATCH);
return null;
}
- 从 converterCache 缓存中获取,如果存在则返回,否则从 converters 中获取,并且加入到
converterCache 缓存中。 - Converters 是 GenericConversionService 内部类,用于管理所有注册的转换器,其内部维护一个 Set 和
Map 的数据结构用于管理转换器。
// GenericConversionService.java#Converters
private final Set<GenericConverter> globalConverters = new LinkedHashSet<>();
private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);
- Converters 对象的 find() 方法,可以查找相应的 GenericConverter。
// GenericConversionService.java#Converters
@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());
// 遍历 sourceCandidates 数组
for (Class<?> sourceCandidate : sourceCandidates) {
// 遍历 targetCandidates 数组
for (Class<?> targetCandidate : targetCandidates) {
// 创建 ConvertiblePair 对象
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
// 获得 GenericConverter 对象
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
@Nullable
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters
// 从 converters 中,获得 converter
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
// 从 globalConverters 中,获得 globalConverter
for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
return globalConverter;
}
}
return null;
}
- find() 方法中,根据 sourceType 和 targetType 查询 Converters 中维护的 Map
是否包括支持的注册类型。- 如果存在返回 GenericConverter,不存在返回 null。
- 获取 GenericConverter 后,可调用 convert() 方法,进行类型转换。
2.1 类型转换器
GenericConverter
- GenericConverter 是一个转换接口,用于在两种或多种类型之间转换的通用型转换器接口。
- GenericConverter 可以支持在多个源/目标类型对之间进行转换,同时也可以在类型转换过程中访问源/目标字段上下文。
- 当更简单的 Converter 或 ConverterFactory 接口足够使用时,通常不应使用此接口。
// GenericConverter.java
public interface GenericConverter {
@Nullable
Set<ConvertiblePair> getConvertibleTypes();
@Nullable
Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
- GenericConverter 接口有很多的转换子类实现(如
ArrayToArrayConverter、ArrayToCollectionConverter、ArrayToObjectConverter、ArrayToStringConverter、StringToArrayConverter
等等)。
// StringToArrayConverter.java
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 对象
TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
Assert.state(targetElementType != null, "No target element type");
// 创建目标数组
Object target = Array.newInstance(targetElementType.getType(), fields.length);
// 遍历 fields 数组,逐个转换
for (int i = 0; i < fields.length; i++) {
String sourceElement = fields[i];
// 执行转换
Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetElementType);
// 设置到 target 中
Array.set(target, i, targetElement);
}
return target;
}
}
- 类型转换体系中,Spring 提供了非常多的类型转换器,除了 GenericConverter,还提供有
Converter、ConditionalConverter、ConverterFactory。
Converter
-
Converter 是一个将 S 类型 的源对象转换为 T 类型 的目标对象的转换器。
-
该接口是线程安全的,可以共享。
// Converter.java
public interface Converter<S, T> {
@Nullable
T convert(S source);
}
- 同样有着很多的转换子类实现。
public static void registerConverters(ConverterRegistry registry) {
DateFormatterRegistrar.addDateConverters(registry);
registry.addConverter(new DateTimeConverters.LocalDateTimeToLocalDateConverter());
registry.addConverter(new DateTimeConverters.LocalDateTimeToLocalTimeConverter());
registry.addConverter(new DateTimeConverters.ZonedDateTimeToLocalDateConverter());
registry.addConverter(new DateTimeConverters.ZonedDateTimeToLocalTimeConverter());
registry.addConverter(new DateTimeConverters.ZonedDateTimeToLocalDateTimeConverter());
registry.addConverter(new DateTimeConverters.ZonedDateTimeToOffsetDateTimeConverter());
registry.addConverter(new DateTimeConverters.ZonedDateTimeToInstantConverter());
registry.addConverter(new DateTimeConverters.OffsetDateTimeToLocalDateConverter());
registry.addConverter(new DateTimeConverters.OffsetDateTimeToLocalTimeConverter());
registry.addConverter(new DateTimeConverters.OffsetDateTimeToLocalDateTimeConverter());
registry.addConverter(new DateTimeConverters.OffsetDateTimeToZonedDateTimeConverter());
registry.addConverter(new DateTimeConverters.OffsetDateTimeToInstantConverter());
registry.addConverter(new DateTimeConverters.CalendarToZonedDateTimeConverter());
registry.addConverter(new DateTimeConverters.CalendarToOffsetDateTimeConverter());
registry.addConverter(new DateTimeConverters.CalendarToLocalDateConverter());
registry.addConverter(new DateTimeConverters.CalendarToLocalTimeConverter());
registry.addConverter(new DateTimeConverters.CalendarToLocalDateTimeConverter());
registry.addConverter(new DateTimeConverters.CalendarToInstantConverter());
registry.addConverter(new DateTimeConverters.LongToInstantConverter());
registry.addConverter(new DateTimeConverters.InstantToLongConverter());
}
ConditionalConverter
- ConditionalConverter 接口用于有条件的类型转换,通过转入的 sourceType 与 targetType
判断转换能否匹配,只有可匹配的转换才可以调用 convert() 方法进行转换。
// ConditionalConverter.java
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
ConverterFactory
- 一个转换工厂,可以将对象从 S 转换为 R 的子类型。
// ConverterFactory.java
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
-
四种不同的转换器承载着不同的转换过程
- Converter:用于 1:1 的 source -> target 类型转换。
- ConverterFactory:用于 1:N 的 source -> target 类型转换。
- GenericConverter:用于 N:N 的 source -> target 类型转换。
- ConditionalConverter:用于有条件的 source -> target 类型转换。
2.2 GenericConversionService
-
ConversionService 接口中定义了两类方法,这两类方法都是在 GenericConversionService 中实现。
- canConvert() 方法,用于判 sourceType 能否转成 targetType 。
- convert() 方法,用于将 source 转成转入的 TargetType 类型实例。
- GenericConversionService 类实现了
-
ConfigurableConversionService 接口,即支持了不同类型之间的转换,也可以对各类型转换器进行管理。
// GenericConversionService.java
@Override
public void addConverter(Converter<?, ?> converter) {
// 1. 获取 ResolvableType 对象,基于 converter.getClass() 类
ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
// 1. 如果获取不到,并且 converter 是 DecoratingProxy 类型,则基于 ((DecoratingProxy) converter).getDecoratedClass() 类
if (typeInfo == null && converter instanceof DecoratingProxy) {
typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);
}
// 如果获取不到,抛出 IllegalArgumentException 异常
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
"Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?");
}
// 2. 封装成 ConverterAdapter 对象,添加到 converters 中
addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
}
- 方法调用流程。
- 步骤 1,根据 converter 获取 ResolvableType 数组。
- 步骤 2,将其与 converter 封装成一个 ConverterAdapter 实
*例,调用 addConverter() 方法,添加到 converters 中。
ResolvableType 用于封装 Java 的 Type 类型。 - ConverterAdapter 是 Converter 的一个适配器, 它实现了 GenericConverter和 ConditionalConverter 两个类型转换器。
addConverter
// GenericConversionService.java
@Override
public void addConverter(GenericConverter converter) {
// 添加到 converters 中
this.converters.add(converter);
// 过期缓存
invalidateCache();
}
- 内部类 Converters 的 add() 方法。
// GenericConversionService.java
public void add(GenericConverter converter) {
// 1. 获得 ConvertiblePair 集合
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
// 2. 如果为空,并且 converter 是 ConditionalConverter 类型,则添加到 globalConverters 中
if (convertibleTypes == null) {
Assert.state(converter instanceof ConditionalConverter,
"Only conditional converters may return null convertible types");
this.globalConverters.add(converter);
} else {
// 3. 通过迭代的方式依次添加 converters 中
for (ConvertiblePair convertiblePair : convertibleTypes) {
// 从 converters 中,获得 ConvertersForPair 对象
ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
// 添加 converter 到 ConvertersForPair 中
convertersForPair.add(converter);
}
}
}
-
方法调用流程。
- 步骤 1,调用 GenericConverter 的 getConvertibleTypes() 方法,获取 ConvertiblePair 集合。
- 步骤 2,如果为空,则加入到 globalConverters 集合中。
- 步骤 3,否则通过迭代的方式依次添加 converters 中。
-
ConvertiblePair 为 source-to-target 的持有者,它持有 source 和 target 的 class 类型。
// GenericConverter.java#ConvertiblePair
final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
......
}
- 在迭代过程中会根据 ConvertiblePair 获取相应的 ConvertersForPair 对象,然后添加 converter
转换器。
// GenericConversionService.java#ConvertersForPair
private static class ConvertersForPair {
private final LinkedList<GenericConverter> converters = new LinkedList<>();
public void add(GenericConverter converter) {
this.converters.addFirst(converter);
}
@Nullable
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
for (GenericConverter converter : this.converters) {
if (!(converter instanceof ConditionalGenericConverter) ||
((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {
return converter;
}
}
return null;
}
}
2.3 DefaultConversionService
- DefaultConversionService 是 ConversionService 的默认实现,为转换体系提供一些默认的转换器。
- DefaultConversionService 构造方法中会添加默认的 Converter。
// DefaultConversionService.java
public DefaultConversionService() {
addDefaultConverters(this);
}
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
- 同时也提供如 addCollectionConverters() 方法,用于注册其他类型的转换器。
2.4 自定义类型转换器
- 自定义类型转换器的步骤。
- 实现 Converter、GenericConverter 或者 ConverterFactory 接口。
- 将该类注册到 ConversionServiceFactoryBean 中。
- ConversionServiceFactoryBean 实现了 InitializingBean 接口的 afterPropertiesSet() 方法。
- 在 Bean 实例化 Bean 阶段,Spring 容器会检查当前 Bean 是否实现了 InitializingBean 接口,如果是则执行相应的初始化方法(【Spring 笔记】InitializingBean 和 init-method 相关整理)。
// ConversionServiceFactoryBean.java
@Override
public void afterPropertiesSet() {
// 1. 创建 DefaultConversionService 对象
this.conversionService = createConversionService();
// 2. 注册到 ConversionServiceFactory 中
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
- 方法调用流程。
- 步骤 1,初始化 conversionService。
- 步骤 2,将定义的 converters 注入到类型转换体系中。
// ConverterRegistry.java
public static void registerConverters(@Nullable Set<?> converters, ConverterRegistry registry) {
if (converters != null) {
// 遍历 converters 数组,逐个注册
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");
}
}
}
}
- ConverterRegistry 是一个 Converter 注册器,定义了一系列注册方法。
- addConverter() 方法将转换器注册到容器中。
- 使用 Spring 容器的时候,Spring 将会自动识别出 IOC 容器中注册的 ConversionService 并且在 Bean 属性注入阶段使用自定义的转换器完成属性的转换。
配置
<?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="TestConversionService"/>
</set>
</property>
</bean>
<bean id="TestConversionService" class="TestConversionService"/>
<bean id="test" class="Test">
<property name="testObj" value="18#testObj"/>
</bean>
</beans>
用例
//TestObj.java
public class TestObj {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//TestConversionService.java
public class TestConversionService implements Converter<String, TestObj> {
@Override
public TestObj convert(String s) {
if (StringUtils.hasLength(s)) {
String[] sources = s.split("#");
TestObj testObj = new TestObj();
testObj.setAge(Integer.parseInt(sources[0]));
testObj.setName(sources[1]);
return testObj;
}
return null;
}
}
//Test.java
public class Test {
private TestObj testObj;
public TestObj getTestObj() {
return testObj;
}
public void setTestObj(TestObj testObj) {
this.testObj = testObj;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Test test = (Test) context.getBean("test");
System.out.println("name:" + test.getTestObj().getName() + ",age:" + test.getTestObj().getAge());
}
}
/* print
name:testObj,age:18
*/