框架简介
在Spring PropertyEditor分析中介绍了如果通过PropertyEditor来进行值转换,已经如何根据自身的需求自定义PropertyEditor,但是Spring的这套处理框架存在几个问题:
- 方法参数是弱类型,需要编写一些类型转化代码,对使用者不友好
- PropertyEditor是有状态的,如果共享实例在高并发条件下会有性能隐患,所以在对bean进行初始化是需要给每个BeanWrapper实例化一个PropertyEditor,如果应用中存在大量的bean那就需要创建大量的PropertyEditor实例,这对GC会造成很大的压力
- 在使用PropertyEditor时,先通过setAsText把待转换的值传到PropertyEditor实例中,保存转换后的值,然后通过getValue再把转后的值取出来,PropertyEditor对这个状态的依赖非常弱,这个value更适合作为方法内部的临时状态,作为一枚菜鸟实在无法理解这种设计,当然这里不是说PropertyEditor的设计有问题,PropertyEditor作为JDK中的一员自然有它的使用场景,只是无法理解Spring对它的使用。
好在Spring3.0之后提供了另外一套值转换框架,可以通过适当的注入替换掉PropertyEditor值转换框架,这套框架就是ConversionService,下面ConversionService框架的主要类图:
ConversionService:对外提供一些值转换服务接口
ConverterRegistry:Convertor注册器
GenericConverter:转换器,ConversionService转换服务最终会把操作委托给具体的转换器,框架中内置了很多转换器
Converter:也是转换器,与GenericConverter相比接口跟简单,使用起来更方便,框架最终会通过适配器把Converter适配成GenericConverter,框架中同样内置了很多这种类型的转换器
ConversionServiceFactoryBean:从名字上可以看出它是一个FactoryBean,可以把转换器注入到它的converters属性中,它会自动创建一个DefaultConversionService实例,可以通过它来向IOC容器注册ConversionService。
自定义转换器
下面通过一个例子来介绍如何实现自定义转换器以及通过代码分析ConversionService框架的实现原理
这个例子实现的功能和Spring PropertyEditor分析中自定义PropertyEditor的例子一样,实现一个字符串到日期对象的转换器。
首先定义一个转换器类,为了方便直接实现Converter,这个接口中只有一个方法并且参数少只有两个,代码非常简单,从代码中可以看到该转换器是无状态的:
public class StringToDateConverter implements Converter<String, Date> {
private String dateFormat;
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
@Override
public Date convert(String source) {
DateFormat df = new SimpleDateFormat(dateFormat);
try {
return df.parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}
}
定义ConversionServiceFactoryBean,并且把自定义的转换器注入,这里只注入了一个转换器,可以按照实际情况注入多个,xml配置片段如下:
<bean id="stringToDateConverter" class="spring.beans.conversion.StringToDateConverter">
<property name="dateFormat" value="yyyy-MM-dd HH:mm:ss"></property>
</bean>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref local="stringToDateConverter" />
</set>
</property>
</bean>
注意bean的名称必须是conversionService,容器会自动检查,这段代码在AbstractApplicationContext类的finishBeanFactoryInitialization方法中:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
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));
}
...
}
上面的converters属性还可以注入GenericConverter类型的对象和ConverterFactory类型的对象,比较简单这里就不列了。
接下定义测试bean:
<bean id="dateBean" class="spring.beans.conversion.DateBean">
<constructor-arg value="2014-03-04 09:21:20"></constructor-arg>
</bean>
junit测试代码:
@Test
public void test() {
BeanFactory context = new ClassPathXmlApplicationContext(
"spring/beans/conversion/conversion.xml");
DateBean dateBean = (DateBean) context.getBean("dateBean");
assertNotNull(dateBean);
assertNotNull(dateBean.getDate());
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = df.format(dateBean.getDate());
assertEquals("2014-03-04 09:21:20", dateStr);
}
测试通过,说明配置成功,转换器起作用了。
实现原理
下面来分析conversionService处理框架的原理:
从上面finishBeanFactoryInitialization方法中的代码可以看到,容器检查到名称conversionService并且类型是ConversionService的对象时会把该ConversionService对象设置到容器(AbstractBeanFactory)的conversionService属性中。
再来看看ConversionServiceFactoryBean中的代码:
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
private Set<?> converters;
private GenericConversionService conversionService;
/**
* Configure the set of custom converter objects that should be added:
* implementing {@link org.springframework.core.convert.converter.Converter},
* {@link org.springframework.core.convert.converter.ConverterFactory},
* or {@link org.springframework.core.convert.converter.GenericConverter}.
*/
public void setConverters(Set<?> converters) {
this.converters = converters;
}
public void afterPropertiesSet() {
this.conversionService = createConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
/**
* Create the ConversionService instance returned by this factory bean.
* <p>Creates a simple {@link GenericConversionService} instance by default.
* Subclasses may override to customize the ConversionService instance that
* gets created.
*/
protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}
// implementing FactoryBean
public ConversionService getObject() {
return this.conversionService;
}
public Class<? extends ConversionService> getObjectType() {
return GenericConversionService.class;
}
public boolean isSingleton() {
return true;
}
}
ConversionServiceFactoryBean实现了InitializingBean接口,所以在bean初始化之后afterPropertiesSet方法会被调用,afterPropertiesSet方法创建了DefaultConversionService对象,这个对象就是转换服务对象,getObject方法把这个对象返回了,也就是说,容器通过getBean("conversionService",...)获取到会是这个服务对象。看下DefaultConversionService类发现注册了很多框架已经实现好的转换器,有兴趣的话可以看看每个转换器里面的代码:
public class DefaultConversionService extends GenericConversionService {
/**
* Create a new {@code DefaultConversionService} with the set of
* {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
*/
public DefaultConversionService() {
addDefaultConverters(this);
}
// static utility methods
/**
* Add converters appropriate for most environments.
* @param converterRegistry the registry of converters to add to (must also be castable to ConversionService)
* @throws ClassCastException if the converterRegistry could not be cast to a ConversionService
*/
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
addFallbackConverters(converterRegistry);
}
// internal helpers
private static void addScalarConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(Enum.class, String.class, new EnumToStringConverter(conversionService));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}
private 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));
}
private static void addFallbackConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter(conversionService));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
}
}
在afterPropertiesSet方法中还调用了ConversionServiceFactory.registerConverters方法把自定义的转换器注册到服务中,看下这个方法的代码:
public static void registerConverters(Set<?> converters, ConverterRegistry registry) {
if (converters != null) {
for (Object converter : converters) {
if (converter instanceof GenericConverter) {
registry.addConverter((GenericConverter) converter);
}
else if (converter instanceof Converter<?, ?>) {
registry.addConverter((Converter<?, ?>) converter);
}
else if (converter instanceof ConverterFactory<?, ?>) {
registry.addConverterFactory((ConverterFactory<?, ?>) converter);
}
else {
throw new IllegalArgumentException("Each converter object must implement one of the " +
"Converter, ConverterFactory, or GenericConverter interfaces");
}
}
}
}
方法中的registry参数就是我们的转换服务对象,代码很简单就是把这些转换器一个一个地添加到服务对象中,从代码中可以看到converters中的元素可以是Converter、GenericConverter、ConverterFactory,来看看这一系列的addConvertor方法:
public void addConverter(Converter<?, ?> converter) {
GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converter, Converter.class);
Assert.notNull(typeInfo, "Unable to the determine sourceType <S> and targetType " +
"<T> which your Converter<S, T> converts between; declare these generic types.");
addConverter(new ConverterAdapter(converter, typeInfo));
}
public void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter) {
GenericConverter.ConvertiblePair typeInfo = new GenericConverter.ConvertiblePair(sourceType, targetType);
addConverter(new ConverterAdapter(converter, typeInfo));
}
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
invalidateCache();
}
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class);
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to the determine sourceType <S> and " +
"targetRangeType R which your ConverterFactory<S, R> converts between; " +
"declare these generic types.");
}
addConverter(new ConverterFactoryAdapter(converterFactory, typeInfo));
}
最终把转换器都保存到converters属性中,这个属性保存的时GenericConverter对象,Converter和ConverterFactory通过适配器装换成GenericConverter对象放到converters属性中,这两个适配器分别是ConverterAdapter、ConverterFactoryAdapter,主要看他们的convert方法:
private final class ConverterAdapter implements ConditionalGenericConverter {
...
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converter.convert(source);
}
...
}
ConverterAdapter的convert方法最终还是委托给Convertor的convert,ConverterFactoryAdapter的convert方法中最终会通过转换器工厂生成一个转换器并调用它的convert,两者都是比较典型的适配器模式
private final class ConverterFactoryAdapter implements ConditionalGenericConverter {
...
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
...
}
上面代码展示了如何注册转换器和转换服务,下面来看看转换服务是怎么调用的。上面代码讲到了转换服务会保存在AbstractBeanFactory的conversionService属性中,在AbstractBeanFactory的initBeanWrapper方法中会把这个转换服务对象设置到BeanWrapper对象中:
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}
BeanWrapper的setPropertyValue最终会调用TypeConverterDelegate类的convertIfNecessary方法,看看这个方法中的一段代码:
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
Object convertedValue = newValue;
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException firstAttemptEx = null;
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
TypeDescriptor targetTypeDesc = typeDescriptor;
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
try {
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
firstAttemptEx = ex;
}
}
}
...
return (T) convertedValue;
}
当值所在的类型没有自定义PropertyEditor时,会调用转换服务,在调用转换服务之前需要调用canConvert判断是否能够被转型服务处理,看看canConvert这个方法的代码:
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "targetType to convert to cannot be null");
if (sourceType == null) {
return true;
}
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
逻辑很简单,检查是否注册过源类型-目标类型的转换器,包括内置的和自定义的,如果不存在这种转换器,说明转换服务无法处理,把值转换动作交给容器的其它组件处理。如果存在这种转换器,那么调用转换服务的convert方法,看下convert方法:
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType,"The targetType to convert to cannot be null");
if (sourceType == null) {
Assert.isTrue(source == null, "The source must be [null] if sourceType == [null]");
return handleResult(sourceType, targetType, convertNullSource(sourceType, targetType));
}
if (source != null && !sourceType.getObjectType().isInstance(source)) {
throw new IllegalArgumentException("The source to convert from must be an instance of " +
sourceType + "; instead it was a " + source.getClass().getName());
}
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
ConversionUtils的invokeConverter方法:
public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType,
TypeDescriptor targetType) {
try {
return converter.convert(source, sourceType, targetType);
}
catch (ConversionFailedException ex) {
throw ex;
}
catch (Exception ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
}
从上面的代码可以看出,值转换动作最终被委托给了转换器的convert方法,在我们上面的示例中,最终会进入StringToDateConverter的convert方法。