7、自定义类型转换器
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:多对多的转换,用于两种或者更多种类型之间转换的通用转换器接口。
<S, T>
1,Converter一对一的转换,将 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)
<S, R>
2,ConverterFactory一对多的转换,将 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 接口灵活和复杂的地方。