前面我们讲了PropertyEditor,是jdk提供的类型转换,spring也有自己的类型转换器,比PropertyEditor稍微强大一点
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Component
public class UserBean {
@Value("xx")
private User user;
public User getUser() {
return user;
}
}
public class String2UserConverter implements ConditionalGenericConverter {
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, User.class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
User user = new User();
user.setName((String) source);
return user;
}
}
@Configuration
public class ConvertConfig {
@Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean factoryBean = new ConversionServiceFactoryBean();
factoryBean.setConverters(Collections.singleton(new String2UserConverter()));
return factoryBean;
}
}
定义如上类,值得注意的是ConversionServiceFactoryBean的方法名一定要是 conversionService
测试
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserBean userBean = context.getBean("userBean", UserBean.class);
System.out.println(userBean);
System.out.println(userBean.getUser());
System.out.println(userBean.getUser().getName());
}
输出
com.shura.beans.UserBean@7225790e
com.shura.convert.User@54a097cc
xx
从上面的结果来看 ConversionService 也能做到类型转换
实现的地方跟PropertyEditor也差不多,下面看源码分析
与PropertyEditor一样,也是在BeanWrapper中实现
// ConstructorResolver类
public BeanWrapper instantiateUsingFactoryMethod(){
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
...
}
// AbstractBeanFactory类
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService()); // 下节分析
registerCustomEditors(bw); // 注册PropertyEditor
}
上面可以看出可以从beanFactory获取ConversionService到BeanWrapper
同样也是在 TypeConverterDelegate 判断是否有ConversionService
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
}
上面就是ConversionService的处理逻辑,那么ConversionService应该也有默认的类型转换器,在哪里设置的呢
进入ConversionServiceFactoryBean,它是一个FactoryBean,真正的对象是getObject返回的GenericConversionService,该属性由createConversionService方法创建,实际上就是实例化了一个DefaultConversionService
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
@Nullable
private Set<?> converters;
@Nullable
private GenericConversionService conversionService;
public void setConverters(Set<?> converters) {
this.converters = converters;
}
@Override
public void afterPropertiesSet() {
this.conversionService = createConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}
@Override
@Nullable
public ConversionService getObject() {
return this.conversionService;
}
@Override
public Class<? extends ConversionService> getObjectType() {
return GenericConversionService.class;
}
}
那么DefaultConversionService做了什么呢
public class DefaultConversionService extends GenericConversionService {
@Nullable
private static volatile DefaultConversionService sharedInstance;
public DefaultConversionService() {
addDefaultConverters(this);
}
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));
}
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());
}
}
就是添加了一堆的默认转换器,到这基本上就能知道spring的转换机制了,以及如何实现自己的转换器
还有个小问题就是前面说了注册ConversionServiceFactoryBean的方法名一定要是 conversionService,Why?
请看如下代码,在实例化非懒加载单例Bean之前就会获取ConversionService设置到beanFactory,而这个名字就只能叫conversionService
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
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));
}
// 实例化非懒加载的单例Bean
beanFactory.preInstantiateSingletons();
}
ConversionService就介绍到这
欢迎关注,学习不迷路!