Spring 中的类型转换 (ConversionService)
在 Spring 有非常多的地方 会使用到类型转换, 比如读取配置文件到上下文时, 实例化 给Bean注入属性时, MVC的参数转换等等
ConversionService
org.springframework.core.convert.ConversionService
package org.springframework.core.convert;
import org.springframework.lang.Nullable;
/**
* A service interface for type conversion. This is the entry point into the convert system.
*
* 用于类型转换的服务接口, 转换系统的入口点。
* 定义了一组用于执行类型转换的方法,包括从一种类型到另一种类型的转换。主要方法包括 `convert`,`canConvert` 和 `canConvert(TypeDescriptor sourceType, TypeDescriptor targetType)`。
*
* Call {@link #convert(Object, Class)} to perform a thread-safe type conversion using this system.
*
* @author Keith Donald
* @author Phillip Webb
* @since 3.0
*/
public interface ConversionService {
/**
* Return {@code true} if objects of {@code sourceType} can be converted to the {@code targetType}.
* <p>If this method returns {@code true}, it means {@link #convert(Object, Class)} is capable
* of converting an instance of {@code sourceType} to {@code targetType}.
* <p>Special note on collections, arrays, and maps types:
* For conversion between collection, array, and map types, this method will return {@code true}
* even though a convert invocation may still generate a {@link ConversionException} if the
* underlying elements are not convertible. Callers are expected to handle this exceptional case
* when working with collections and maps.
* @param sourceType the source type to convert from (may be {@code null} if source is {@code null})
* @param targetType the target type to convert to (required)
* @return {@code true} if a conversion can be performed, {@code false} if not
* @throws IllegalArgumentException if {@code targetType} is {@code null}
*/
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
/**
* Return {@code true} if objects of {@code sourceType} can be converted to the {@code targetType}.
* The TypeDescriptors provide additional context about the source and target locations
* where conversion would occur, often object fields or property locations.
* <p>If this method returns {@code true}, it means {@link #convert(Object, TypeDescriptor, TypeDescriptor)}
* is capable of converting an instance of {@code sourceType} to {@code targetType}.
* <p>Special note on collections, arrays, and maps types:
* For conversion between collection, array, and map types, this method will return {@code true}
* even though a convert invocation may still generate a {@link ConversionException} if the
* underlying elements are not convertible. Callers are expected to handle this exceptional case
* when working with collections and maps.
* @param sourceType context about the source type to convert from
* (may be {@code null} if source is {@code null})
* @param targetType context about the target type to convert to (required)
* @return {@code true} if a conversion can be performed between the source and target types,
* {@code false} if not
* @throws IllegalArgumentException if {@code targetType} is {@code null}
*/
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
/**
* Convert the given {@code source} to the specified {@code targetType}.
* @param source the source object to convert (may be {@code null})
* @param targetType the target type to convert to (required)
* @return the converted object, an instance of targetType
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null}
*/
@Nullable
<T> T convert(@Nullable Object source, Class<T> targetType);
/**
* Convert the given {@code source} to the specified {@code targetType}.
* <p>Delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)}
* and encapsulates the construction of the source type descriptor using
* {@link TypeDescriptor#forObject(Object)}.
* @param source the source object
* @param targetType the target type
* @return the converted value
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null}
* @since 6.1
*/
@Nullable
default Object convert(@Nullable Object source, TypeDescriptor targetType) {
return convert(source, TypeDescriptor.forObject(source), targetType);
}
/**
* Convert the given {@code source} to the specified {@code targetType}.
* The TypeDescriptors provide additional context about the source and target locations
* where conversion will occur, often object fields or property locations.
* @param source the source object to convert (may be {@code null})
* @param sourceType context about the source type to convert from
* (may be {@code null} if source is {@code null})
* @param targetType context about the target type to convert to (required)
* @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType}
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null},
* or {@code sourceType} is {@code null} but source is not {@code null}
*/
@Nullable
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}
in short ConversionService 用于类型转换的服务接口, 它是转换类型的入口点, 但是真正的转换逻辑功能 则由 Converter 和 GenericConverter 接口的实现处理的
Converte 和 GenericConverter
Converte
一个简单的转换器, 将泛型S 转到 泛型T, 它是单一单向的转换, 适合需要简单转换的场景
org.springframework.core.convert.converter.Converter
package org.springframework.core.convert.converter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* A converter converts a source object of type {@code S} to a target of type {@code T}.
*
* 一个简单的转换器, 将泛型S 转到 泛型T, 它是单一单向的转换, 适合需要简单转换的场景
* <p>Implementations of this interface are thread-safe and can be shared.
* <p>Implementations may additionally implement {@link ConditionalConverter}.
*
* @author Keith Donald
* @author Josh Cummings
* @since 3.0
* @param <S> the source type
* @param <T> the target type
*/
@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);
/**
* Construct a composed {@link Converter} that first applies this {@link Converter}
* to its input, and then applies the {@code after} {@link Converter} to the
* result.
* @param after the {@link Converter} to apply after this {@link Converter}
* is applied
* @param <U> the type of output of both the {@code after} {@link Converter}
* and the composed {@link Converter}
* @return a composed {@link Converter} that first applies this {@link Converter}
* and then applies the {@code after} {@link Converter}
* @since 5.3
*/
default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
Assert.notNull(after, "'after' Converter must not be null");
return (S s) -> {
T initialResult = convert(s);
return (initialResult != null ? after.convert(initialResult) : null);
};
}
}
GenericConverter
GenericConverter 适合用于更复杂的类型转换, 在两个或多个类型之间进行转换的通用转换器接口;
org.springframework.core.convert.converter.GenericConverter
package org.springframework.core.convert.converter;
import java.util.Set;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Generic converter interface for converting between two or more types.
* 用于在两个或多个类型之间进行转换的通用转换器接口。
*
* 它的灵活性在于GenericConverter可以支持多个 source/target 类型对之间的转换 (请参见 {@link #getConvertibleTypes()})。
* 此外,在类型转换过程中,GenericConverter实现可以访问 source/target {@link TypeDescriptor 字段上下文}。
* 这允许解析源字段和目标字段元数据,例如注释和泛型信息,这些元数据可用于影响转换逻辑。
*
* <p>This is the most flexible of the Converter SPI interfaces, but also the most complex.
* It is flexible in that a GenericConverter may support converting between multiple source/target
* type pairs (see {@link #getConvertibleTypes()}). In addition, GenericConverter implementations
* have access to source/target {@link TypeDescriptor field context} during the type conversion
* process. This allows for resolving source and target field metadata such as annotations and
* generics information, which can be used to influence the conversion logic.
*
* <p>This interface should generally not be used when the simpler {@link Converter} or
* {@link ConverterFactory} interface is sufficient.
*
* <p>Implementations may additionally implement {@link ConditionalConverter}.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
* @see TypeDescriptor
* @see Converter
* @see ConverterFactory
* @see ConditionalConverter
*/
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());
}
}
}
当需要定义复杂的 Converter 实现时,可以使用 GenericConverter 接口,GenericConverter 并不是 Converter 的子接口,而是一个独立的顶级接口,翻译成中文就是“通用转换器”**
ConversionService 的实现 GenericConversionService
在 org.springframework.core.convert.support.GenericConversionService#converters 存的是 GenericConverter
org.springframework.core.convert.support.GenericConversionService#addConverter(java.lang.Class<S>, java.lang.Class<T>, org.springframework.core.convert.converter.Converter<? super S,? extends T>)
添加 Converter
@Override
public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) {
//这里将 Converter 适配了一下 ConverterAdapter
addConverter(new ConverterAdapter(
converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType)));
}添加 GenericConverter
org.springframework.core.convert.support.GenericConversionService#addConverter(org.springframework.core.convert.converter.GenericConverter)
@Override
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
invalidateCache();
}自定义类型转换
可以通过实现 Converter、ConverterFactory 或 GenericConverter 接口来创建自定义转换器,并将其注册到 ConversionServiceFactoryBean 中
若要使用自己的自定义转换器补充或覆盖默认转换器,请设置 converters 属性。属性值可以实现任何Converter、ConverterFactory或GenericConverter接口。
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>It is also common to use a ConversionService within a Spring MVC application. See Conversion and Formatting in the Spring MVC chapter.
相关源码
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
/**
* Finish the initialization of this context's bean factory, * initializing all remaining singleton beans. */protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
/**
给context 添加 几个内置的 ConversionService; 在容器中凡是用到类型转换就用它们
*/
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no BeanFactoryPostProcessor
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}