7.盘点springmvc的常用接口之Converter(下篇)###

前两章介绍了ConverterConverterFactory以及衍生出来的GenericConverterConditionalGenericConverter,最后附录罗列了Spring自带的各种转换器。这么多的转换器,我们开发者可不想在要使用转换器时还自己来查找转换器使用。所以为了统一调用Converter进行类型转换,Spring友好地为我们提供了一个org.springframework.core.convert.ConversionService接口。我们靠猜都知道查询一个转换器,肯定是通过原类型和目标类型来查的,而且在查到转换器后我们会通过convert方法来执行转换过程。

接口说明

那么这个ConversionService的接口看起来应该是这样子:

public interface ConversionService {

	boolean canConvert(Class<?> sourceType, Class<?> targetType);
  
	boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

	<T> T convert(Object source, Class<T> targetType);

	Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

一对canConvert来判断是否匹配原类型和目标类型,

一对convert执行转换过程。其中TypeDescriptor是类型的描述,里面包含该种类型的值、实际类型等等信息。

ConversionService接口它还有一个好基友org.springframework.core.convert.converter.ConverterRegistry,一般这两个接口都是同时出现,ConverterRegistry的作用是对转换器的管理,做一个统一的注册工作。它包含一系列的增删转换器的操作。接口如下:

public interface ConverterRegistry {

	void addConverter(Converter<?, ?> converter);

	void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter);

	void addConverter(GenericConverter converter);

	void addConverterFactory(ConverterFactory<?, ?> converterFactory);

	void removeConvertible(Class<?> sourceType, Class<?> targetType);

方法名字比较直白就不翻译了。

对于众多转换器的注册管理,查询执行转换工作就给我们这两个接口未免实现起来也有点困难。所以Spring已经给我们提供了一个实现org.springframework.core.convert.support.GenericConversionService,它实现了ConversionServiceConverterRegistry接口,但是它不能直接拿来使用。因为不能通过类似下面的配置往里面添加我们自己的转换器。

<mvc:annotation-driven conversion-service="myConversionService"/>  
  
<bean id="myConversionService" class="com.demo.mvc.component.MyConversionService">
	<property name="customConverters">
  		<set>
          <bean class="com.demo.mvc.component.TelephoneConverter"/>
      	</set>
  	</property>
</bean>

所以这个地方我们可以用到一款经典的设计模式,那就是“装饰模式”,在GenericConversionService上套上我们自己的装饰器。

package com.demo.mvc.component;

import java.util.Set;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.GenericConversionService;

import lombok.Getter;
import lombok.Setter;

public class MyConversionService implements ConversionService {

	@Autowired
	private GenericConversionService conversionService;

	@Getter
	@Setter
	private Set<?> customConverters;

	@PostConstruct
	public void afterInjectCustomConverters() {
		if (customConverters != null) {
			for (Object converter : customConverters) {
				if (converter instanceof Converter<?, ?>) {
					conversionService.addConverter((Converter<?, ?>) converter);
				} else if (converter instanceof ConverterFactory<?, ?>) {
					conversionService.addConverterFactory((ConverterFactory<?, ?>) converter);
				} else if (converter instanceof GenericConverter) {
					conversionService.addConverter((GenericConverter) converter);
				}
			}
		}
	}

	@Override
	public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
		return conversionService.canConvert(sourceType, targetType);
	}

	@Override
	public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return conversionService.canConvert(sourceType, targetType);
	}

	@Override
	public <T> T convert(Object source, Class<T> targetType) {
		return conversionService.convert(source, targetType);
	}

	@Override
	public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		return conversionService.convert(source, sourceType, targetType);
	}
}

通过customConverters集合可以把我们自定义的转换器设置进来,再调用afterInjectCustomConverters方法设置到GenericConversionService中,之后的其它操作都是调用原来的GenericConversionService的方法。

除了以上这种自己实现的装饰器外,Spring已经为我们提供了一个既可以使用GenericConversionService又可以添加自己的转换器的类,那就是org.springframework.context.support.ConversionServiceFactoryBean。这个类提供了一个converters属性让我们设置自己的转换器。在对象初始化完成之后它会new一个GenericConversionService对象,并往GenericConversionService中注册converters属性指定的Converter和Spring自身已经实现了的默认Converter,之后每次返回的都是这个GenericConversionService对象。注册时,我们可以这样配置:

<mvc:annotation-driven conversion-service="conversionService"/>  
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">  
    <property name="converters">  
        <set>  
            <bean class="com.demo.mvc.component.TelephoneConverter"/>  
        </set>  
    </property>  
</bean>

GenericConversionService的子类还有FormattingConversionService,这个类既可以转换类型又可以格式化。它也有一个FactoryBean来辅助注册:

<mvc:annotation-driven conversion-service="conversionService"/>  
   
    <bean id="conversionService"     class="org.springframework.format.support.FormattingConversionServiceFactoryBean">  
          <property name="converters">  
             <set>  
                 <bean class="com.demo.mvc.component.TelephoneConverter"/>  
             </set>  
          </property>  
</bean>