架構簡介
在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方法。