跳至主要內容

7、自定义类型转换器

安图新大约 5 分钟

7、自定义类型转换器

一,ConversionService 转换服务

一般来说,用户可以使⽤用内置的或者⾃己实现 Converter 接⼝来实现类型转换,ConversionService 类接口内部调用 Converter 接口实现类来实现类型转换。
 

1,ConversionService 接口

类型转换的服务接口,这是转换系统的入口,调用convert(Object, Class)进行一次线程安全的类型转换。

public interface ConversionService {



	// 判断能否进行类型转换
	boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
	boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

	// 类型转换,获取合适的转换器进行类型的转换,默认是DefaultConversionService,也可以是自定义的
	@Nullable
	<T> T convert(@Nullable Object source, Class<T> targetType);
	@Nullable
	Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

}

2,ConverterRegistry 接口

对转换器进行注册(完成类型转换器的增删操作):

public interface ConverterRegistry {



	// 添加转换器
	void addConverter(Converter<?, ?> converter);
	<S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter);
	void addConverter(GenericConverter converter);

	// 增加转换器的工厂类
	void addConverterFactory(ConverterFactory<?, ?> factory);

    // 移除转换器
	void removeConvertible(Class<?> sourceType, Class<?> targetType);

}

3,ConfigurableConversionService 接口

此接口集成了 ConversionService 和 ConverterRegistry 两个接口,集成两个接口的功能:

public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {



}

4,GenericConversionService 类

通用的类型转换实现类,适用于大部分的转换情况,转换器服务类的骨干实现。

public class GenericConversionService implements ConfigurableConversionService {



    // 省略部分代码

    private final Converters converters = new Converters();

    // 管理在服务中注册的所有转换器
    private static class Converters {


        private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);

        // 省略部分代码
    }

    // 组建一个源到目的的组合
    final class ConvertiblePair {



		private final Class<?> sourceType;
		private final Class<?> targetType;

        // 省略部分代码
    }
}

GenericConversionService类是类型转换服务的具体核心实现,其管理了所有注册的类型转换器 Converter,对外提供了注册,转换等核心接口,是具体功能的实现者。

5,DefaultConversionService 类

封装了系统的默认Converter注册:

// 对一系列的converter进行注册
public class DefaultConversionService extends GenericConversionService {



	@Nullable
	private static volatile DefaultConversionService sharedInstance;
	/**
	 * Create a new {@code DefaultConversionService} with the set of
	 * {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
	 */
	public DefaultConversionService() {


		addDefaultConverters(this);
	}
	/**
	 * Return a shared default {@code ConversionService} instance,
	 * lazily building it once needed.
	 */
	public static ConversionService getSharedInstance() {


		DefaultConversionService cs = sharedInstance;
		if (cs == null) {


			synchronized (DefaultConversionService.class) {


				cs = sharedInstance;
				if (cs == null) {


					cs = new DefaultConversionService();
					sharedInstance = cs;
				}
			}
		}
		return cs;
	}

	/**
	 * Add converters appropriate for most environments.
	 */
	public static void addDefaultConverters(ConverterRegistry converterRegistry) {


		addScalarConverters(converterRegistry);
		addCollectionConverters(converterRegistry);

		converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new StringToTimeZoneConverter());
		converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
		converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

		converterRegistry.addConverter(new ObjectToObjectConverter());
		converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new FallbackObjectToStringConverter());
		converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
	}

	/**
	 * Add common collection converters.
	 */
	public 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));

		converterRegistry.addConverter(new StreamConverter(conversionService));
	}

	private static void addScalarConverters(ConverterRegistry converterRegistry) {


		converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());

		converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
		converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharacterConverter());
		converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new NumberToCharacterConverter());
		converterRegistry.addConverterFactory(new CharacterToNumberFactory());

		converterRegistry.addConverter(new StringToBooleanConverter());
		converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));

		converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));

		converterRegistry.addConverter(new StringToLocaleConverter());
		converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharsetConverter());
		converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCurrencyConverter());
		converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToPropertiesConverter());
		converterRegistry.addConverter(new PropertiesToStringConverter());

		converterRegistry.addConverter(new StringToUUIDConverter());
		converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
	}

}

二,自定义 ConversionService

一般系统会默认使用DefaultConversionService,来提供类型转换功能,我们也可以自定义ConversionService

创建MyConersionService

@Component
public class MyConversionService extends DefaultConversionService {


}

xml 配置,注意:这里 id 必须是 conversionService,否则 spring 源码识别不到。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <bean id="conversionService" class="com.bobo.MyConversionService"></bean>
</beans>

spring 源码:spring 会在 refresh()方法的 finishBeanFactoryInitialization(beanFactory)方法的开头对自定义的 MyConversionService 进行注册:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {



    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));
		}
    }

    // 省略部分代码
}

三,Converter 转换器

ConversionService只是个 Service,对于每个类型转换的操作,它并不是最终的操作者,它会将相应操作交给对应类型的转换器。而在实际项目中,由于业务复杂,对类型转换的要求也不一样,因此 spring 提供了几个接口来方便自定义转换器。

  • Converter<S, T>:一对一的转换,将 S 类型转换为 T 类型;
  • ConverterFactory<S, R>:一对多的转换,将 S 类型转换为 R 的子类;
  • GenericConverter:多对多的转换,用于两种或者更多种类型之间转换的通用转换器接口。

1,Converter<S, T>

一对一的转换,将 S 类型转换为 T 类型;

@FunctionalInterface
public interface Converter<S, T> {



	// 将S类型转换成T类型
	@Nullable
	T convert(S source);

}

起一个 SpringBoot 项目,定义 MyConverter 定义 String 到 Person 的转换:

Person.java

@Data
@ToString
@NoArgsConstructor
public class Person {


    private String name;
    private int age;

    public Person(String name, int age) {


        this.name = name;
        this.age = age;
    }
}

MyConverter.java

@Component
public class MyConverter implements Converter<String, Person> {


    @Override
    public Person convert(String source) {


        System.out.println("----------------------"+source+"---------------");
        String[] split = source.split("-");
        return new Person(split[0], Integer.valueOf(split[1]));
    }
}

HelloController.java

@RestController
public class HelloController {



    @RequestMapping("/hello")
    @ResponseBody
    public String hello(@RequestParam("name") Person person){


        System.out.println(person);
        return person.toString();
    }
}

输入地址:http://localhost:8080/hello?name=bo-12
返回:Person(name=bo, age=12)

2,ConverterFactory<S, R>

一对多的转换,将 S 类型转换为 R 的子类:

MyConverterFactory.java

public class MyConverterFactory implements ConverterFactory<String, Person> {



    @Override
    public <T extends Person> Converter<String, T> getConverter(Class<T> targetType) {


        System.out.println("targetType:" + targetType);
        return new StringToPerson<>(targetType);
    }

    private static final class StringToPerson<T extends Person> implements Converter<String, T> {



        private final Class<T> targetType;

        public StringToPerson(Class<T> targetType) {


            this.targetType = targetType;
        }

        @Override
        public T convert(String source) {


            String[] split = source.split("-");
            if (Man.class == targetType) {


                return (T) new Man(split[0], Integer.valueOf(split[1]));
            } else if (Woman.class == targetType) {


                return (T) new Woman(split[0], Integer.valueOf(split[1]));
            }
            return null;
        }
    }
}

MySpringBootApplication.java

@SpringBootApplication
public class MySpringBootApplication {



    public static void main(String[] args) {


        SpringApplication.run(MySpringBootApplication.class, args);
    }

    @Bean
    public GenericConversionService getDefaultConversionService(@Autowired GenericConversionService genericConversionService) {


        genericConversionService.addConverterFactory(new MyConverterFactory());
        System.out.println("类型转换已加入!");
        return genericConversionService;
    }

}

HelloControll.java

@RestController
public class HelloController {



    @RequestMapping("/hello")
    @ResponseBody
    public Object hello(@RequestParam("name") Man person){


        System.out.println(person);
        return person.toString();
    }
}

输入:http://localhost:8080/hello?name=bo-12
输出:Person(name=bo, age=12)

3,GenericConverter

GenericConverter 接口是所有的 Converter 接口中最灵活也是最复杂的一个类型转换接口。像我们之前介绍的 Converter 接口只支持从一个原类型转换为一个目标类型;ConverterFactory 接口只支持从一个原类型转换为一个目标类型对应的子类型;而 GenericConverter 接口支持在多个不同的原类型和目标类型之间进行转换,这也就是 GenericConverter 接口灵活和复杂的地方。