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

图解

相关推荐
魔道不误砍柴功18 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_23418 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨21 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity3 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java
caridle3 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^3 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋33 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花3 小时前
【JAVA基础】Java集合基础
java·开发语言·windows