Spring源码阅读之类型转换

Spring源码阅读之类型转换

一、Spring 类型转换实现

  • 基于 JavaBeans 接口的类型转换器实现
    • 基于 java.beans.PropertyEditor 接口扩展
  • Spring 3.0+ 通用类型转换实现
    • Spring 3.0 引入 core.convert 包,它提供了一个通用的类型转换系统。这个系统定义了一个用于类型转换逻辑的 SPI(Service Provider Interface,服务发现机制) SPI和一个用于在运行时执行类型转换的 API 。在Spring容器中,你可以使用这一套转换作为 ProeprtyEditor 替代实现,将外部化的Bean属性值字符串转换为所需要的类型。

二、使用场景

场景 基于JavaBeans类型转换 Spring 3.0+ 通用类型转换
数据绑定 YES YES
BeanWrapper YES YES
Bean 属性类型转换 YES YES
外部化属性类型转换 NO YES

三、基于 JavaBeans 接口的类型转换

3.1 PropertyEditor概述

  • 核心职责
    • Spring 类型的内容转换为目标类型对象
  • 扩展原理
    • Spring 框架将文本内容传递到 ProtyEditor 实现的 setAsText(String) 方法。
    • PropertyEditor#setAsText(String) 方法实现将 String 类型转化为目标类型的对象。
    • 将目标类型的对象传入 PropertyEditor#setValue(Object) 方法。
    • PropertyEditor#setValue(Object) 方法实现需要临时存储传入对象。
    • Spring 框架将通过 PropertyEditort#getValue() 获取类型转换后的对象。

3.2 Spring 内建 PropertyEditor 扩展

org.springframework.beans.propertyeditors 包下。

转换场景 实现类
String -> Byte数组 org.springframework.beans.propertyeditors.ByteArrayPropertyEditor
String -> Char org.springframework.beans.propertyeditors.CharacterEditor
String -> Char数组 org.springframework.beans.propertyeditors.CharArrayPropertyEditor
String -> Charset org.springframework.beans.propertyeditors.CharsetEditor
String -> Class org.springframework.beans.propertyeditors.ClassEditor
String -> Currency org.springframework.beans.propertyeditors.CurrencyEditor

Spring源码阅读之类型转换_第1张图片

3.2 Spring 内建 ByteArrayPropertyEditor 示例:

public class ByteArrayPropertyEditor extends PropertyEditorSupport {

	@Override
	public void setAsText(@Nullable String text) {
		setValue(text != null ? text.getBytes() : null);
	}

	@Override
	public String getAsText() {
		byte[] value = (byte[]) getValue();
		return (value != null ? new String(value) : "");
	}

}

可以看到,其实自定义扩展 PropertyEditor 非常容易。

3.4 自定义 PropertyEditor 扩展

  1. 扩展 java.beans.PropertyEditorSupport
/**
 * 字符串转换为Proeprties类型编辑器
 */
public class StringToPropertiesPropertyEditor extends PropertyEditorSupport implements PropertyEditor {

	/**
	 * #1 实现 setAsText方法
	 * @param text
	 * @throws IllegalArgumentException
	 */
	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		Properties properties = new Properties();
		try {
			properties.load(new StringReader(text));
		} catch (IOException e) {
			throw new IllegalArgumentException(e);
		}

		// 存储
		setValue(properties);
	}

	@Override
	public String getAsText() {
		Properties properties = (Properties)getValue();

		StringBuilder builder = new StringBuilder();

		for (Map.Entry<Object, Object> entry : properties.entrySet()) {
			builder.append(entry.getKey()).append("=").append(entry.getValue());
		}

		return builder.toString();
	}
}

  1. 实现 org.springframework.bean.PropertyEditorRegister
    1. 实现 registerCustomEditors(org.springframework.beans.PropertyEditorRegistry) 方法
    2. PropertyEditorRegister 实现注册为 Spring Bean
  2. org.springframwork.beans.PropertyEditorRegistry 注册自定义 PropertyEditor 实现
    1. 通用类型实现 registerCustomEditor(Class, PropertyEditor)
    2. Java Bean 属性类型实现: registerCustomEditor(Class, String, PropertyEditor)
/**
 * 自定义Properties属性转换注册器
 */
public class CustomizedPropertyEditorRegistrar implements PropertyEditorRegistrar {
	@Override
	public void registerCustomEditors(PropertyEditorRegistry registry) {
		// 1. 通用类型属性
		// 2. Java Bean属性转换
		registry.registerCustomEditor(User.class, "context", new StringToPropertiesPropertyEditor());
	}
}

3.5 PropertyEditor 的设计缺陷

  1. 违反 单一职责原则
    1. 除了拥有类型转换功能,还包含 Java Bean 事件和 Java GUI 交互
  2. 类型只能为 string
  3. 除了 实现类命名 可以表达语义,实现类无法感知目标转换类型

四、Spring 3.0+ 通用类型转换接口

4.1 概述

由于 JDKPropertyEditor 的设计缺陷,因此 Spring 重新设计了新的一套转换API。

  • 类型 转换接口( 小而美单一类型转换
    • org.springframework.core.convert.converter.Converter
  • 通用类型 转换接口( 复合类型通用类型集合等
    • org.springframework.core.convert.converter.GenericConverter
  • 类型条件 转换接口( 条件判断前置条件
    • org.springframework.core.convert.converter.ConditionalConverter
  • 综合类型 转换接口( 大而全综合
    • org.springframework.core.convert.converter.ConditionalGenericConverter

4.2 Converter

4.2.1 功能

只对一对类型(源/目标对象)进行转换,将源对象 S 转换为目标对象 T

4.2.2 局限性

  • 缺少 Source TypeTarget Type 前置 判断。用户可以实现 ConditionalConverter 接口,此接口参数为 TypeDescriptor ,该类持有 ResolvableType ,因此可以解析 泛型 相关的信息,因此, 匹配判断 阶段可以更 灵活宽泛
  • 仅能转换单一的 Source TypeTarget Type 。可以使用 GenericConverter 接口代替。不过这个接口更加复杂。 因此,简单的类型转换使用此接口进行扩展
@FunctionalInterface
public interface Converter<S, T> {

	/**
	 * 转换源对象类型为 S 到 目标对象 T
	 * @param source 非空源对象,必须为 S 类型实例
	 * @return 		 被转换生成的对象,必须为 T 类型实例,可能为null
	 * @throws IllegalArgumentException if the source cannot be converted to the desired target type
	 */
	@Nullable
	T convert(S source);

}

4.3 ConverterFactory

当您需要集中整个类层次结构的转换逻辑时(例如,当从 String 转换到 Enum 对象时) ,您可以实现 ConverterFactory

/**
 * 转换器工厂,可以将对象从类型S转换为R的子类型。
 * 配合{@link ConditionalConverter}食用更佳
 *
 * @param  由工厂创建的源类型转换器
 * @param  由工厂转换后的目标类型,为目标类型的父类。比如对于目标类型为{@link Number},表示的就是number的子集
 */
public interface ConverterFactory<S, R> {

	/**
	 * 获取转换器以从S转换为目标类型T,其中T也是R的实例。
	 */
	<T extends R> Converter<S, T> getConverter(Class<T> targetType);

}

比如 A<-B<-C 表示C继承B、B继承A,如果S为 String ,R为A,则说明此工厂可以将String转换为A、B、C任意类型,从而实现单一类型转换多类型(此类型为父类型的子集)。

4.4 GenericConverter

4.4.1 概述

核心要素 说明
使用场景 用于 复合 类型转换场景在比如 CollectionMapArray
转换范围 Set getConvertibleTypes()
配对类型 org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
转换方法 convert(Object, TypeDescriptor, TypeDescriptor)
类型描述 org.springframework.core.convert.TypeDescriptor

4.4.2 接口

/**
 * 通用转换器接口,可在两种或多种类型之间进行转换。
 *
 * 这是Converter SPI接口中最灵活的,也是最复杂的。
 * 灵活的是,GenericConverter可能支持在多个源/目标类型对之间进行转换(参考{@link #getConvertibleTypes()})。
 *
 * 此外,GenericConverter的实现类可以在类型转换过程中可以访问{@link TypeDescriptor field context}。
 * 用以解析源和目标字段元数据,比如注解和泛型信息,从而影响转换逻辑。
 *
 * 当较简单的{@link Converter} 或{@link ConverterFactory}接口满足需求,通常不应使用此接口。
 * 实现可能另外实现{@link ConditionalConverter}
 */
public interface GenericConverter {

	/**
	 * 返回此转换器可以在其之间转换的源和目标类型。
	 * 每个条目都是可转换的源到目标类型对。
	 * 对于 {@link ConditionalConverter conditional converters} ,
	 * 此方法可能返回{@code null}以指示应考虑所有源到目标对
	 */
	@Nullable
	Set<ConvertiblePair> getConvertibleTypes();

	/**
	 * 转换 源对象 到 被{@code TypeDescriptor}所描述的目标对象
	 */
	@Nullable
	Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

	/**
	 * 持有一对源对象类型->目标对象类型
	 */
	final class ConvertiblePair {
		private final Class<?> sourceType;
		private final Class<?> targetType;

		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;
		}
        // getter/setter equals hashCode method
	}

}

4.4.3 接口解析

  • 核心方法
    • convert(Object, TypeDescriptor, TypeDescriptor)
  • 配对类型
    • org.springframework.core.convert.converter.GenericConverter#ConvertiblePair
  • 类型描述
    • org.springframework.core.convert.TypeDescriptor
  • 功能
    • 多对多 类型转换

4.4.4 Spring 内建示例

/**
 * 将一个集合转换为数组
 * #1 创建一个新的数组,数组类型为目标类型,数组大小为集合大小
 * #2 将每个集合元素设置到数组中
 * #3 如果需要的话,将会使用转换器转换集合中的元素
 *
 * @author Keith Donald
 * @author Juergen Hoeller
 * @since 3.0
 */
final class CollectionToArrayConverter implements ConditionalGenericConverter {

	private final ConversionService conversionService;


	public CollectionToArrayConverter(ConversionService conversionService) {
		this.conversionService = conversionService;
	}


	@Override
	public Set<ConvertiblePair> getConvertibleTypes() {
		return Collections.singleton(new ConvertiblePair(Collection.class, Object[].class));
	}

	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(),
				targetType.getElementTypeDescriptor(), this.conversionService);
	}

	@Override
	@Nullable
	public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		if (source == null) {
			return null;
		}
		Collection<?> sourceCollection = (Collection<?>) source;
		TypeDescriptor targetElementType = targetType.getElementTypeDescriptor();
		Assert.state(targetElementType != null, "No target element type");
		Object array = Array.newInstance(targetElementType.getType(), sourceCollection.size());
		int i = 0;
		for (Object sourceElement : sourceCollection) {
            // convert和GenericConvert可以相互配合
			Object targetElement = this.conversionService.convert(sourceElement,
					sourceType.elementTypeDescriptor(sourceElement), targetElementType);
			Array.set(array, i++, targetElement);
		}
		return array;
	}

}

4.4.5 小结

GenericConverter 的一个很好例子是在 Java数组集合 之间转换的转换器。相对于 Converter 接口而言,此接口功能更强大。适用于复合类型转换需求,完成 N->N 的类型转换。
对于复合类型,需要递归解析为最终的基本类型数据,所以在解析过程中与 Converter 简单转换接口配合使用,复杂(GenericConverter )- 简单(Converter )- 复杂(GenericConverter )。

4.5 ConditionalConverter

GenericConverter局限性有:

  • 缺少 Source TypeTarget Type 前置判断
  • 单一类型转换实现复杂

于是出现了这么一个接口 ConditionalConverter ,在进行类型转换之前,通过该接口的 matches 方法进行一个 前置判断 操作,如果返回 true 表明可以进行类型转换,返回 false 则表明不能进行类型转换。

4.5.1 接口

/**
 * 允许{@link Converter},{@link GenericConverter}或{@link ConverterFactory}基于源和目标 {@link TypeDescriptor}的属性有条件地执行转换操作。
 *
 * 通常用于根据字段或类级别特征(例如注释或方法)的存在来选择性地匹配自定义转换逻辑。
 *
 * 例如,当从字符串字段转换为日期字段时,如果目标字段也已使用@DateTimeFormat进行注释,则实现可能返回true。
 * 再举一个例子,当从String字段转换为{@code Account}字段时,如果目标Account类定义了公共的静态findAccount(String)方法,则实现可能返回true。
 */
public interface ConditionalConverter {

	/**
	 * 是否应选择当前正在考虑的从{@code sourceType}到{@code targetType}的转换?
	 */
	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);

}

4.5.2 小结

该接口定义了方法 matches ,用于 前置匹配 ,在转换之前可以通过该方法判断能否将源对象转换为目标对象。此外, TypeDescriptor 类提供关于类的详情,包括 注解(annotatedElement)泛型(ResolvableType) ,因此,可以做 更精确、更广泛 的类型转换操作。

4.6 Spring 内建实现类

Spring 与新的通用类型转换接口相关的实现类在 org.springframework.core.convert.support 包下:

Spring源码阅读之类型转换_第2张图片

4.7 ConversionService

4.7.1 接口

package org.springframework.core.convert;

public interface ConversionService {

    boolean canConvert(Class<?> sourceType, Class<?> targetType);

    <T> T convert(Object source, Class<T> targetType);

    boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}

4.7.2 Spring 内建默认实现

实现类型 说明
GenericConversionService 通用 ConversionService 模板 实现, 不内置转化器实现
DefaultConversionService 基础 ConversionService 实现, 内置常用转化器实现 ,可以满足大多数场景应用
FormattingConversionService 通用 Formatter + GenericConversionService 实现,不内置转化器和Formatter实现
DefaultFormattingConversionService DefaultConversionService + 格式化 实现(如: JSR-354 Money & Currency)

4.7.3 DefaultConversionService

public class DefaultConversionService extends GenericConversionService {

	@Nullable
	private static volatile DefaultConversionService sharedInstance;


	/**
	 * Create a new {@code DefaultConversionService} with the set of
	 * {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
	 */
	public DefaultConversionService() {
		addDefaultConverters(this);
	}

    // 单例实现
	public static ConversionService getSharedInstance() {
		DefaultConversionService cs = sharedInstance;
		if (cs == null) {
			synchronized (DefaultConversionService.class) {
				cs = sharedInstance;
				if (cs == null) {
					cs = new DefaultConversionService();
					sharedInstance = cs;
				}
			}
		}
		return cs;
	}

	// 添加默认转换器
	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));
	}

	// 添加常用集合转换器
	public static void addCollectionConverters(ConverterRegistry converterRegistry) {
		ConversionService conversionService = (ConversionService) converterRegistry;

		converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
		converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
		converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
		converterRegistry.addConverter(new MapToMapConverter(conversionService));

		converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));

		converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

		converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));

		converterRegistry.addConverter(new StreamConverter(conversionService));
	}
	
    // 添加刻度转换器
	private static void addScalarConverters(ConverterRegistry converterRegistry) {
		converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());

		converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
		converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharacterConverter());
		converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new NumberToCharacterConverter());
		converterRegistry.addConverterFactory(new CharacterToNumberFactory());

		converterRegistry.addConverter(new StringToBooleanConverter());
		converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));

		converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));

		converterRegistry.addConverter(new StringToLocaleConverter());
		converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharsetConverter());
		converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCurrencyConverter());
		converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToPropertiesConverter());
		converterRegistry.addConverter(new PropertiesToStringConverter());

		converterRegistry.addConverter(new StringToUUIDConverter());
		converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
	}

}

4.7.4 小结

ConversionService 定义了一个统一的 API,这是进入转换系统的 入口点 。通过调用 convert(Object, Class) 方法使用此系统执行线程安全类型转换。


大多数 ConversionService 实现还实现了 ConverterRegistry ,它为注册转换器提供了一个 SPI 。在内部, ConversionService 实现 委托 其注册的转换器执行类型转换逻辑。
core.convert.support 包提供了一个健壮的 ConversionService 实现。 GenericConversionService 是适合在大多数环境中使用的通用实现。 ConversionServiceFactory 为创建通用的 ConversionService 配置提供了一个便捷的工厂。

4.8 Spring 内建类型转换器

转换场景 实现类所在包名(package)
日期/时间相关 org.springframework.format.datetime
Java8 日期/时间相关 org.springframework.format.datetime.standard
通用实现 org.springframework.core.convert.support

4.9 TypeConverter

4.9.1 概述

TypeConverterspring-beans 项目下的接口,用以在 BeanFactory 实例化的过程中进行类型转换操作,而该接口真正实现是通过代理类 org.springframework.beans.TypeConverterDelegate 来完成。

4.9.2 类图

Spring源码阅读之类型转换_第3张图片


从上面类图可以看到, BeanWrapperImplSpring 非常重要的一个实现类,通过继承 TypeConverter 接口获得了 类型转换 的能力,在 Spring 工厂进行 属性填充 这一环境, BeanWrapperImpl 也是调用 TypeConverter 相应的 API 完成属性类型转换。

4.9.3 接口

public interface TypeConverter {

	/**
	 * 从String到任何类型的转换通常将使用PropertyEditor类的{@code setAsText}方法或
     * ConversionService中的Spring Converter
	 * conver if necessary: 如果可以转换就转换~
	 * 这个方法整合了两个转换方式:
	 *  ① 扩展JDK接口 {@link java.beans.PropertyEditor}
	 *  ② Spirng3.0+通用实现 {@link org.springframework.core.convert.ConversionService}
	 */
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;

	/**
	 * 将值转换为所需的类型(如果需要,则从字符串)。
	 * 从String到任何类型的转换通常将使用PropertyEditor类的{@code setAsText}方法或
     * ConversionService中的Spring Converter。
	 */
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
			@Nullable MethodParameter methodParam) throws TypeMismatchException;

	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
			throws TypeMismatchException;

	@Nullable
	default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
			@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {

		throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
	}

}

4.9.4 TypeConverterSupport

TypeConverterSupport 是实现 TypeConverter 的抽象类,里面持有 TypeConverterDelegate 的引用,该类是最终完成类型转换的 代理类

public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {

	@Nullable
	TypeConverterDelegate typeConverterDelegate;


	@Override
	@Nullable
	public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException {
		return convertIfNecessary(value, requiredType, TypeDescriptor.valueOf(requiredType));
	}
    ...
}

4.10 TypeConverterDelegate

这是在项目 spring-beans 类型转换的代理类,在它内部持有 PropertyEditorRegistrySupport 引用,该类包含 beanFactory 工厂中的 PropertyEditor 以及 ConversionService ,包括 Spring 内建和 自定义 两种类型,他们是完成最终类型转换的实现类。

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {

	/**
	 * Boolean flag controlled by a {@code spring.xml.ignore} system property that instructs Spring to
	 * ignore XML, i.e. to not initialize the XML-related infrastructure.
	 * 

The default is "false". */ private static final boolean shouldIgnoreXml = SpringProperties.getFlag("spring.xml.ignore"); // 通过AbstractApplicationContext#finishBeanFactoryInitialization方法依赖查找当前 beanFactory, // 获取类型为ConversionSerivce.class且名称为conversionService的实现类 @Nullable private ConversionService conversionService; private boolean defaultEditorsActive = false; private boolean configValueEditorsActive = false; // 默认属性编辑器 @Nullable private Map<Class<?>, PropertyEditor> defaultEditors; // 覆盖默认属性编辑器 @Nullable private Map<Class<?>, PropertyEditor> overriddenDefaultEditors; // 自定义属性编辑器 @Nullable private Map<Class<?>, PropertyEditor> customEditors; @Nullable private Map<String, CustomEditorHolder> customEditorsForPath; @Nullable private Map<Class<?>, PropertyEditor> customEditorCache; ... }

五、扩展 Spring 类型转换器

5.1 扩展步骤

  1. 从以下接口中挑选适合自己的转换器接口(每种接口应对不同的场景,没有说哪个最好,只有合适):
    1. org.springframework.core.convert.converter.Converter
    2. org.springframework.core.convert.converter.GenericConverter
    3. org.springframework.core.convert.converter.ConditionalConverter
  2. 注册转换器实现
    1. 通过 ConversionServiceFactoryBean Spring Bean
    2. 通过 org.spring.framework.core.convert.ConversionService API

5.2 示例: 将Properties对象转换为字符串

① 定义Bean实例

public class User {
	private Properties context;

	private String contextAsText;

	public String getContextAsText() {
		return contextAsText;
	}

	public void setContextAsText(String contextAsText) {
		this.contextAsText = contextAsText;
	}

	public Properties getContext() {
		return context;
	}

	public void setContext(Properties context) {
		this.context = context;
	}


	@Override
	public String toString() {
		return "User{" +
				"context=" + context +
				", contextAsText='" + contextAsText + '\'' +
				'}';
	}
}

②-① 定义自定义转换器 Converter

自定义类型转换器一般实现 ConditionalGenericConverter ,可以在类型转换前进行 匹配判断matches 前置动作。

/**
 * Properties->String转换器
 */
public class PropertiesToStringConverter implements ConditionalGenericConverter {
	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return Properties.class.equals(sourceType.getObjectType())
				&& String.class.equals(targetType.getObjectType());
	}

	@Override
	public Set<ConvertiblePair> getConvertibleTypes() {
		return Collections.singleton(new ConvertiblePair(Properties.class, String.class));
	}

	@Override
	public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		Properties properties = (Properties)source;

		StringBuilder builder = new StringBuilder();

		for (Map.Entry<Object, Object> entry : properties.entrySet()) {
			builder.append(entry.getKey()).append("=").append(entry.getValue()).append(";");
		}

		return builder.toString();
	}
}

③-② 使用 XML 配置 ConversionServiceFactoryBean

请注意: ConversionServiceFactoryBean 名称必须为 conversionService ,否则在Spring容器 实例化所有非延迟单例 阶段无法通过依赖查找获取到自定义的 ConversionService 实例对象。相关代码:

String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
/**
 * △ 完成此上下文的Bean工厂的初始化,实现化剩余所有单例Bean
 */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // #1 为此上下文初始化转换类(判断)
    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));
    }
    ...
}

<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:util="http://www.springframework.org/schema/util"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        https://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

	<bean class="com.clarencezero.c2_core.convert.CustomizedPropertyEditorRegistrar" />

	
	<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
		<property name="converters">
			<bean class="com.clarencezero.c2_core.convert.PropertiesToStringConverter" />
		property>
	bean>

	<util:properties id="context" >
		<prop key="id">1prop>
		<prop key="name">clarencezero2prop>
	util:properties>

	<bean id="user" class="com.clarencezero.c2_core.convert.User" >
		<property name="context">
			<value> 
				name=clarencezero
				id=1
			value>
		property>
		<property name="contextAsText" ref="context" />
	bean>

beans>

③-② 或在配置类中使用注解:

@Bean
public ConversionServiceFactoryBean conversionService() {
    ConversionServiceFactoryBean factoryBean = new ConversionServiceFactoryBean();
    Set<ConditionalGenericConverter> conversionServiceSet = new HashSet<>(1);
    conversionServiceSet.add(new PropertiesToStringConverter());
    factoryBean.setConverters(conversionServiceSet);
    return factoryBean;
}

六、简单的UML流程图

Spring源码阅读之类型转换_第4张图片

七、总结

  1. 类型转换有两种,分别是
    1. 通过扩展 JDK PropertyEditor
    2. Spring 3.0+ 引入 core.convert 包,它提供了一个 通用的类型转换系统
  2. 两种不同类型 API 扩展、注册到 BeanFactory 都非常简单和方便。 core.convert 相对于 PropertyEditor 提供更丰富的接口以及更丰富的类型转换。
  3. 项目 spring-beans 也定义了与转换相关的 APITypeConverter ,他的底层通过 TypeConverterDelegate 代理类完成类型转换操作。

你可能感兴趣的:(Spring源码阅读,spring)