框架简介

在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框架的主要类图:

Spring Data最新版本_Convertor

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方法。