天天看點

Spring conversion架構架構簡介自定義轉換器實作原理

架構簡介

在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 conversion架構架構簡介自定義轉換器實作原理

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