一、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 |
… | … |
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 扩展
扩展
java.beans.PropertyEditorSupport
类 ```java /**字符串转换为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
builder.append(entry.getKey()).append("=").append(entry.getValue());
}
return builder.toString(); } }
2. 实现 `org.springframework.bean.PropertyEditorRegister`
1. 实现 `registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)` 方法
1. 将 `PropertyEditorRegister` 实现注册为 `Spring Bean`
3. 向 `org.springframwork.beans.PropertyEditorRegistry` 注册自定义 `PropertyEditor` 实现
1. 通用类型实现 `registerCustomEditor(Class<?>, PropertyEditor)`
1. `Java Bean` 属性类型实现: `registerCustomEditor(Class<?>, String, PropertyEditor)`
```java
/**
* 自定义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 的设计缺陷
- 违反
单一职责原则
- 除了拥有类型转换功能,还包含
Java Bean
事件和Java GUI
交互
- 除了拥有类型转换功能,还包含
- 类型只能为
string
- 除了
实现类命名
可以表达语义,实现类无法感知目标转换类型四、Spring 3.0+ 通用类型转换接口
4.1 概述
由于JDK
的PropertyEditor
的设计缺陷,因此Spring
重新设计了新的一套转换API。
类型
转换接口(小而美
、单一类型转换
)org.springframework.core.convert.converter.Converter
通用类型
转换接口(复合类型
、通用类型
、集合等
)org.springframework.core.convert.converter.GenericConverter
类型条件
转换接口(条件判断
、前置条件
)org.springframework.core.convert.converter.ConditionalConverter
综合类型
转换接口(大而全
、综合
)缺少
Source Type
和Target Type
前置
判断。用户可以实现ConditionalConverter
接口,此接口参数为TypeDescriptor
,该类持有ResolvableType
,因此可以解析泛型
相关的信息,因此,匹配判断
阶段可以更灵活
、宽泛
。仅能转换单一的
Source Type
和Target Type
。可以使用GenericConverter
接口代替。不过这个接口更加复杂。 因此,简单的类型转换使用此接口进行扩展 ```java @FunctionalInterface public interface Converter{/**
- 转换源对象类型为 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);
}
<a name="NVXHl"></a>
## 4.3 ConverterFactory
当您需要集中整个类层次结构的转换逻辑时(例如,当从 `String` 转换到 `Enum` 对象时) ,您可以实现 `ConverterFactory` 。
```java
/**
* 转换器工厂,可以将对象从类型S转换为R的子类型。
* 配合{@link ConditionalConverter}食用更佳
*
* @param <S> 由工厂创建的源类型转换器
* @param <R> 由工厂转换后的目标类型,为目标类型的父类。比如对于目标类型为{@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 概述
核心要素 | 说明 |
---|---|
使用场景 | 用于 复合 类型转换场景在比如 Collection 、 Map 、 Array 等 |
转换范围 | Set<ConvertiblePair> 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
功能
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;
}
}
<a name="6KEPU"></a>
### 4.4.5 小结
`GenericConverter` 的一个很好例子是在 `Java数组` 和 `集合` 之间转换的转换器。相对于 `Converter` 接口而言,此接口功能更强大。适用于复合类型转换需求,完成 `N->N` 的类型转换。<br />对于复合类型,需要递归解析为最终的基本类型数据,所以在解析过程中与 `Converter` 简单转换接口配合使用,复杂(`GenericConverter` )- 简单(`Converter` )- 复杂(`GenericConverter` )。
<a name="gMJVA"></a>
## 4.5 ConditionalConverter
GenericConverter局限性有:
- 缺少 `Source Type` 和 `Target Type` 前置判断
- 单一类型转换实现复杂
于是出现了这么一个接口 `ConditionalConverter` ,在进行类型转换之前,通过该接口的 `matches` 方法进行一个 `前置判断` 操作,如果返回 `true` 表明可以进行类型转换,返回 `false` 则表明不能进行类型转换。
<a name="HInLs"></a>
### 4.5.1 接口
```java
/**
* 允许{@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
包下:
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 概述
TypeConverter
是 spring-beans
项目下的接口,用以在 BeanFactory
实例化的过程中进行类型转换操作,而该接口真正实现是通过代理类 org.springframework.beans.TypeConverterDelegate
来完成。
4.9.2 类图
从上面类图可以看到, BeanWrapperImpl
是 Spring
非常重要的一个实现类,通过继承 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.
* <p>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 扩展步骤
- 从以下接口中挑选适合自己的转换器接口(每种接口应对不同的场景,没有说哪个最好,只有合适):
org.springframework.core.convert.converter.Converter
org.springframework.core.convert.converter.GenericConverter
org.springframework.core.convert.converter.ConditionalConverter
注册转换器实现
- 通过
ConversionServiceFactoryBean
Spring Bean
通过
org.spring.framework.core.convert.ConversionService
API
5.2 示例: 将Properties对象转换为字符串
① 定义Bean实例
```java 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 + '\'' +
'}';
}
}
<a name="q64j4"></a>
### ②-① 定义自定义转换器 Converter
自定义类型转换器一般实现 `ConditionalGenericConverter` ,可以在类型转换前进行 `匹配判断matches` 前置动作。
```java
/**
* 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));
}
...
}
<?xml version="1.0" encoding="UTF-8"?>
<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" />
<!--三、声明 ConversionServiceFactoryBean-->
<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">1</prop>
<prop key="name">clarencezero2</prop>
</util:properties>
<bean id="user" class="com.clarencezero.c2_core.convert.User" >
<property name="context">
<value> <!--Proeprties 类型-->
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流程图
七、总结
- 类型转换有两种,分别是
- 通过扩展 JDK
PropertyEditor
Spring 3.0+
引入core.convert
包,它提供了一个通用的类型转换系统
。
- 通过扩展 JDK
- 两种不同类型
API
扩展、注册到BeanFactory
都非常简单和方便。core.convert
相对于PropertyEditor
提供更丰富的接口以及更丰富的类型转换。 - 项目
spring-beans
也定义了与转换相关的API
—TypeConverter
,他的底层通过TypeConverterDelegate
代理类完成类型转换操作。