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 |
Spring
类型的内容转换为目标类型对象Spring
框架将文本内容传递到 ProtyEditor
实现的 setAsText(String)
方法。PropertyEditor#setAsText(String)
方法实现将 String
类型转化为目标类型的对象。PropertyEditor#setValue(Object)
方法。PropertyEditor#setValue(Object)
方法实现需要临时存储传入对象。Spring
框架将通过 PropertyEditort#getValue()
获取类型转换后的对象。在 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
内建 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
非常容易。
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();
}
}
org.springframework.bean.PropertyEditorRegister
registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)
方法PropertyEditorRegister
实现注册为 Spring Bean
org.springframwork.beans.PropertyEditorRegistry
注册自定义 PropertyEditor
实现
registerCustomEditor(Class>, PropertyEditor)
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());
}
}
单一职责原则
Java Bean
事件和 Java GUI
交互string
实现类命名
可以表达语义,实现类无法感知目标转换类型由于 JDK
的 PropertyEditor
的设计缺陷,因此 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
Converter
只对一对类型(源/目标对象)进行转换,将源对象 S
转换为目标对象 T
。
Source Type
和 Target Type
前置
判断。用户可以实现 ConditionalConverter
接口,此接口参数为 TypeDescriptor
,该类持有 ResolvableType
,因此可以解析 泛型
相关的信息,因此, 匹配判断
阶段可以更 灵活
、 宽泛
。Source Type
和 Target 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);
}
当您需要集中整个类层次结构的转换逻辑时(例如,当从 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任意类型,从而实现单一类型转换多类型(此类型为父类型的子集)。
核心要素 | 说明 |
---|---|
使用场景 | 用于 复合 类型转换场景在比如 Collection 、 Map 、 Array 等 |
转换范围 | Set |
配对类型 | org.springframework.core.convert.converter.GenericConverter.ConvertiblePair |
转换方法 | convert(Object, TypeDescriptor, TypeDescriptor) |
类型描述 | org.springframework.core.convert.TypeDescriptor |
/**
* 通用转换器接口,可在两种或多种类型之间进行转换。
*
* 这是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
}
}
convert(Object, TypeDescriptor, TypeDescriptor)
org.springframework.core.convert.converter.GenericConverter#ConvertiblePair
org.springframework.core.convert.TypeDescriptor
多对多
类型转换/**
* 将一个集合转换为数组
* #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;
}
}
GenericConverter
的一个很好例子是在 Java数组
和 集合
之间转换的转换器。相对于 Converter
接口而言,此接口功能更强大。适用于复合类型转换需求,完成 N->N
的类型转换。
对于复合类型,需要递归解析为最终的基本类型数据,所以在解析过程中与 Converter
简单转换接口配合使用,复杂(GenericConverter
)- 简单(Converter
)- 复杂(GenericConverter
)。
GenericConverter局限性有:
Source Type
和 Target Type
前置判断于是出现了这么一个接口 ConditionalConverter
,在进行类型转换之前,通过该接口的 matches
方法进行一个 前置判断
操作,如果返回 true
表明可以进行类型转换,返回 false
则表明不能进行类型转换。
/**
* 允许{@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);
}
该接口定义了方法 matches
,用于 前置匹配
,在转换之前可以通过该方法判断能否将源对象转换为目标对象。此外, TypeDescriptor
类提供关于类的详情,包括 注解(annotatedElement)
、 泛型(ResolvableType)
,因此,可以做 更精确、更广泛
的类型转换操作。
Spring
与新的通用类型转换接口相关的实现类在 org.springframework.core.convert.support
包下:
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);
}
实现类型 | 说明 |
---|---|
GenericConversionService | 通用 ConversionService 模板 实现, 不内置转化器实现 |
DefaultConversionService | 基础 ConversionService 实现, 内置常用转化器实现 ,可以满足大多数场景应用 |
FormattingConversionService | 通用 Formatter + GenericConversionService 实现,不内置转化器和Formatter实现 |
DefaultFormattingConversionService | DefaultConversionService + 格式化 实现(如: JSR-354 Money & Currency) |
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());
}
}
ConversionService
定义了一个统一的 API,这是进入转换系统的 入口点
。通过调用 convert(Object, Class)
方法使用此系统执行线程安全类型转换。
大多数 ConversionService
实现还实现了 ConverterRegistry
,它为注册转换器提供了一个 SPI
。在内部, ConversionService
实现 委托
其注册的转换器执行类型转换逻辑。core.convert.support
包提供了一个健壮的 ConversionService
实现。 GenericConversionService
是适合在大多数环境中使用的通用实现。 ConversionServiceFactory
为创建通用的 ConversionService
配置提供了一个便捷的工厂。
转换场景 | 实现类所在包名(package) |
---|---|
日期/时间相关 | org.springframework.format.datetime |
Java8 日期/时间相关 | org.springframework.format.datetime.standard |
通用实现 | org.springframework.core.convert.support |
TypeConverter
是 spring-beans
项目下的接口,用以在 BeanFactory
实例化的过程中进行类型转换操作,而该接口真正实现是通过代理类 org.springframework.beans.TypeConverterDelegate
来完成。
从上面类图可以看到, BeanWrapperImpl
是 Spring
非常重要的一个实现类,通过继承 TypeConverter
接口获得了 类型转换
的能力,在 Spring
工厂进行 属性填充
这一环境, BeanWrapperImpl
也是调用 TypeConverter
相应的 API
完成属性类型转换。
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");
}
}
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));
}
...
}
这是在项目 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;
...
}
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
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 + '\'' +
'}';
}
}
自定义类型转换器一般实现 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;
}
PropertyEditor
Spring 3.0+
引入 core.convert
包,它提供了一个 通用的类型转换系统
。API
扩展、注册到 BeanFactory
都非常简单和方便。 core.convert
相对于 PropertyEditor
提供更丰富的接口以及更丰富的类型转换。spring-beans
也定义了与转换相关的 API
– TypeConverter
,他的底层通过 TypeConverterDelegate
代理类完成类型转换操作。