Spring IOC 源码学习 环境准备

Spring 中的类型转换 (ConversionService)

在 Spring 有非常多的地方 会使用到类型转换, 比如读取配置文件到上下文时, 实例化 给Bean注入属性时, MVC的参数转换等等

ConversionService

https://docs.spring.io/spring-framework/reference/core/validation/convert.html#core-convert-ConversionService-API

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();
	}

自定义类型转换

https://docs.spring.io/spring-framework/reference/core/validation/convert.html#core-convert-ConversionService-API

可以通过实现 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();  
}