Sping源码(九)—— Bean的初始化(非懒加载)— ConversionService

序言

经过前面一系列的加载、解析等准备工作,此刻refresh方法的执行已经来到了尾声,接下来我们用几篇文章着重的介绍一下Bean的初始化

代码

着重看refresh()主流程中的finishBeanFactoryInitialization()方法。
finishBeanFactoryInitialization

方法首先会判断beanFactory中是否包含ConversionService,并设置属性。

java 复制代码
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		// 如果包含ConversionService,则赋值给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));
		}
		//省略部分源码...
	}

扩展:ConversionService

我们经常会在页面进行各种各样的输入 abcd,1234等 这些输入是如何变换成指定的类型的呢?。

看看beanFacroty设置的ConversionService是什么?如果我们想要自定义ConversionService改如何实现?

ConversionService

Spring的提供的ConversionService接口,里面包含判断canConvert()方法判断是否可以进行类型转换,convert方法进行类型的转换。接口的实现类有很多,我们主要看DefaultConversionService即可。

java 复制代码
/**
 * 类型转换服务
 */
public interface ConversionService {

	/**
	 *  判断sourceType是否能转换成targetType
	 */
	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);
}

DefaultConversionService
DefaultConversionService下分别有addDefaultConvertersaddCollectionConvertersaddScalarConverters三个方法,会在DefaultConversionService初始化时添加各种类型的Converter,以便我们使用时直接拿。我们看一下源码:

java 复制代码
public class DefaultConversionService extends GenericConversionService {

	@Nullable
	private static volatile DefaultConversionService sharedInstance;

	public DefaultConversionService() {
		addDefaultConverters(this);
	}

	public static void addDefaultConverters(ConverterRegistry converterRegistry) {
		addScalarConverters(converterRegistry);
		addCollectionConverters(converterRegistry);
		
		converterRegistry.addConverter(new StringToTimeZoneConverter());
		//添加各种Converter。。。。
	}

	public static void addCollectionConverters(ConverterRegistry converterRegistry) {
		ConversionService conversionService = (ConversionService) converterRegistry;
		
		converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
		//添加各种Converter。。。。
	}

	private static void addScalarConverters(ConverterRegistry converterRegistry) {
		//省略部分源码
		converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
		//添加各种ConverterFacroty。。。。
	}
}

Converter接口

addDefaultConverters方法。
StrinToimeZoneConverter

类继承了Converter,而Converter接口中的convert方法会将参数S 转换成 T类型

java 复制代码
class StringToTimeZoneConverter implements Converter<String, TimeZone> {

	@Override
	public TimeZone convert(String source) {
		return StringUtils.parseTimeZoneString(source);
	}
}

@FunctionalInterface
public interface Converter<S, T> {

	/** 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);
}

ConditionalGenericConverter

addCollectionConverters方法。
ArrayToCollectionConverter

类继承关系
ArrayToCollectionConverter -》ConditionalGenericConverter -》 GenericConverter(ConditionalConverter)

其中ConditionalConverter接口中的match方法可以理解成@Confitional注解,根据传入的 sourceType 和 targetType 来判断是否符合转换。

GenericConverter中的convert方法,会将 source 通过 sourceType的描述 转换成 targetType类型

java 复制代码
final class ArrayToCollectionConverter implements ConditionalGenericConverter {
	@Override
	public Set<ConvertiblePair> getConvertibleTypes() {
		return Collections.singleton(new ConvertiblePair(Object[].class, Collection.class));
	}

	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return ConversionUtils.canConvertElements(
				sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
	}
	
	public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		// 。。。。。。。省略
	}
}
java 复制代码
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {

}
java 复制代码
public interface ConditionalConverter {

	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
java 复制代码
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);


	/**
	 * suorceType 和 targetType的键值对
	 * Holder for a source-to-target class pair.
	 */
	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 this.sourceType;
		}

		public Class<?> getTargetType() {
			return this.targetType;
		}
	}
}

TypeDescriptor

包含常见基础的类型类和包装类。

java 复制代码
public class TypeDescriptor implements Serializable {

	private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];

	private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<>(32);

	private static final Class<?>[] CACHED_COMMON_TYPES = {
			boolean.class, Boolean.class, byte.class, Byte.class, char.class, Character.class,
			double.class, Double.class, float.class, Float.class, int.class, Integer.class,
			long.class, Long.class, short.class, Short.class, String.class, Object.class};

	static {
		for (Class<?> preCachedClass : CACHED_COMMON_TYPES) {
			commonTypesCache.put(preCachedClass, valueOf(preCachedClass));
		}
	}
}

ConverterFactory

StringToNumberConverterFactory

java 复制代码
final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {

	@Override
	public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
		return new StringToNumber<>(targetType);
	}

	private 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.isEmpty()) {
				return null;
			}
			return NumberUtils.parseNumber(source, this.targetType);
		}
	}
}

ConverterFactory

ConverterFactory转换会支持将 S 转换成 T 类型 (T instance of R)。

java 复制代码
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);
}

图解

相关推荐
Good Note8 分钟前
Golang的静态强类型、编译型、并发型
java·数据库·redis·后端·mysql·面试·golang
我就是我3521 小时前
记录一次SpringMVC的406错误
java·后端·springmvc
向哆哆1 小时前
Java应用程序的跨平台性能优化研究
java·开发语言·性能优化
ekkcole1 小时前
windows使用命令解压jar包,替换里面的文件。并重新打包成jar包,解决Failed to get nested archive for entry
java·windows·jar
handsomestWei2 小时前
java实现多图合成mp4和视频附件下载
java·开发语言·音视频·wutool·图片合成视频·视频附件下载
全栈若城2 小时前
03 Python字符串与基础操作详解
java·开发语言·python
伯牙碎琴2 小时前
二、Spring Framework基础:IoC(控制反转)和DI(依赖注入)
java·spring·log4j
菲力蒲LY2 小时前
输入搜索、分组展示选项、下拉选取,全局跳转页,el-select 实现 —— 后端数据处理代码,抛砖引玉展思路
java·前端·mybatis
南宫生2 小时前
力扣每日一题【算法学习day.130】
java·学习·算法·leetcode
!!!5253 小时前
Java实现斗地主-做牌以及对牌排序
java·算法