Spring官网阅读(十四)Spring中的BeanWrapper及类型转换(3)

简介: Spring官网阅读(十四)Spring中的BeanWrapper及类型转换(3)

类型转换


TypeConverterDelegate


这个类我们只看一个核心方法,如下:

class TypeConverterDelegate {
  private final PropertyEditorRegistrySupport propertyEditorRegistry;
  @Nullable
  private final Object targetObject;
  public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
      @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
    // 查看是否为当前这个类型配置了定制的PropertyEditor
    PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
    ConversionFailedException conversionAttemptEx = null;
    // 获取当前容器中的类型转换业务类
    ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
        // 在这里可以看出,Spring底层在进行类型转换时有两套机制
        // 1.首选的是采用PropertyEditor
        // 2.在没有配置PropertyEditor的情况下,会采用conversionService
    if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
      TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
      if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
        try {
                    // 通过conversionService进行类型转换
          return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
        }
        catch (ConversionFailedException ex) {
          // fallback to default conversion logic below
          conversionAttemptEx = ex;
        }
      }
    }
    Object convertedValue = newValue;
    // 配置了定制的属性编辑器,采用PropertyEditor进行属性转换
    if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
      if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
          convertedValue instanceof String) {
        TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
        if (elementTypeDesc != null) {
          Class<?> elementType = elementTypeDesc.getType();
          if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
            convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
          }
        }
      }
      if (editor == null) {
                // 没有配置定制的属性编辑器,采用默认的属性编辑器
        editor = findDefaultEditor(requiredType);
      }
            // 采用属性编辑器进行转换,需要注意的是,默认情况下PropertyEditor只会对String类型的值进行类型转换
      convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
    }
        // .....
    return (T) convertedValue;
  }
}

从上面的代码中我们可以知道,Spring在实现类型转换时,有两套机制,第一套机制依赖于PropertyEditor,第二套机制依赖于ConversionService。关于属性编辑器PropertyEditor我们之前已经介绍过了,主要进行的是String到Object的转换,正因为如此,属性编辑器进行类型转换有很大的局限性,所以Spring又推出了一套ConversionService的体系。


ConversionService体系


1、Converter


接口定义

package org.springframework.core.convert.converter;
// 将一个S类型的数据转换成T类型
public interface Converter<S, T> {
    T convert(S source);
}


这个接口只能进行一对一的转换,S->T


2、ConverterFactory


接口定义

public interface ConverterFactory<S, R> {
    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

利用这个转换工厂,我们可以进行一对多的转换,以Spring内置的一个转换器为例:

final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
  @Override
  public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
    return new StringToEnum(ConversionUtils.getEnumType(targetType));
  }
  private class StringToEnum<T extends Enum> implements Converter<String, T> {
    private final Class<T> enumType;
    public StringToEnum(Class<T> enumType) {
      this.enumType = enumType;
    }
    @Override
    public T convert(String source) {
      if (source.isEmpty()) {
        // It's an empty enum identifier: reset the enum value to null.
        return null;
      }
      return (T) Enum.valueOf(this.enumType, source.trim());
    }
  }
}

通过传入不同的枚举类型,我们可以从这个工厂中获取到不同的转换器,并把对应的String类型的参数转换成对应的枚举类型数据。


可以看到,通过ConverterFactory,我们能实现一对多的类型转换S->(T extends R)


3、GenericConverter


接口定义

public interface GenericConverter {
    // 获取能够转换的ConvertiblePair的集合,这个对象就是一组可以进行转换的类型
  @Nullable
  Set<ConvertiblePair> getConvertibleTypes();
    // 根据源数据类型转换成目标类型数据
  @Nullable
  Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
  final class ConvertiblePair {
    // 源数据类型
    private final Class<?> sourceType;
    // 目标数据类型
    private final Class<?> targetType;
    // .....省略部分代码
  }
}

相比于前面的Converter以及ConverterFactory,这个接口就更加牛逼了,使用它能完成多对多的转换。因为它内部保存了一个能够进行转换的ConvertiblePair的集合,每个ConvertiblePair代表一组能进行转换的数据类型。同时,这个接口相比我们前面介绍的两个接口,更加的复杂,所以一般情况也不推荐使用这个接口,没有非常必要的话,最好是使用上面两种


一般GenericConverter会与ConditionalGenericConverter配合使用,其接口定义如下:

public interface ConditionalConverter {
  // 判断是否需要对目标类型转换到原类型,返回true的话代表要执行转换,否则不执行转换
    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
// 结合了上面两个接口
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

我们来看下Spring内部的一个实际使用的例子:

final class StringToCollectionConverter implements ConditionalGenericConverter {
  private final ConversionService conversionService;
  @Override
  public Set<ConvertiblePair> getConvertibleTypes() {
    return Collections.singleton(new ConvertiblePair(String.class, Collection.class));
  }
  @Override
  public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
    return (targetType.getElementTypeDescriptor() == null ||
                // 根据conversionService来判断是否需要执行转换
        this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor()));
  }
  @Override
  @Nullable
  public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
      // 这里会借助conversionService来执行转换
  }
}

可以看到,最终的实现还是借助了ConversionService,那么ConversionService到底是啥呢?


4、ConversionService


接口定义

public interface ConversionService {
    // 判断是否能进行类型转换
  boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
  boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
    // 进行类型转换
  @Nullable
  <T> T convert(@Nullable Object source, Class<T> targetType);
  @Nullable
  Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}

UML类图


image.png

一般来说,实现了ConversionService跟ConverterRegistry会结合使用,对于这种xxxRegistry我相信大家猜都能猜出来它是干什么的了,代码如下:


ConverterRegistry

// 就是在添加Converter或者ConverterFactory
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);
}

ConfigurableConversionServic

// 单纯的整合了ConversionService以及ConverterRegistry的功能
public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {
}

GenericConversionService


这个类已经是一个具体的实现类,可以直接使用,但是我们一般不会直接使用它,而是使用它的子类DefaultConversionService,因为子类提供了很多默认的转换器。


DefaultConversionService

public class DefaultConversionService extends GenericConversionService {
  @Nullable
  private static volatile DefaultConversionService sharedInstance;
  public DefaultConversionService() {
    addDefaultConverters(this);
  }
  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;
  }
  public static void addDefaultConverters(ConverterRegistry converterRegistry) {
    addScalarConverters(converterRegistry);
    addCollectionConverters(converterRegistry);
    converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
    ......
  }
  public static void addCollectionConverters(ConverterRegistry converterRegistry) {
    ......
  }
  private static void addScalarConverters(ConverterRegistry converterRegistry) {
    converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
    ......
  }
}

相比其父类GenericConversionService,这个子类默认添加了很多的转换器,这样可以极大的方便我们进行开发,所以一般情况下我们都会使用这个类。


如何配置ConversionService


讲了这么多,那么如何往容器中配置一个ConversionService呢?我们需要借助Spring提供的一个ConversionServiceFactoryBean。其代码如下:

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;
  }
  @Override
  public boolean isSingleton() {
    return true;
  }
}

这个类的实现逻辑很简单,ConversionServiceFactoryBean创建完成后,在进行初始化时调用afterPropertiesSet方法,创建一个DefaultConversionService,然后将提供的converters全部注册到这个DefaultConversionService中。所以我们进行如下的配置就行了

<bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            # 提供自己的converter,可以覆盖默认的配置
            <bean class="example.MyCustomConverter"/>
        </set>
    </property>
</bean>

总结


这篇文章中,我们学习了BeanWrapper,知道一个BeanWrapper其实就是一个Bean的包装器,它对Bean包装的目的是为了能操纵Bean中的属性,所以它同时需要具备获取以及设置Bean中的属性能力,所以它也必须是一个属性访问器(PropertyAccessor),另外为了将各种不同类型的配置数据绑定到Bean的属性上,那么它还得具备属性转换的能力,因为它还得是一个类型转换器(TypeConverter)。


通过上面的分析,我们知道Spring中将类型转换的功能都委托给了一个TypeConverterDelegate,这个委托类在进行类型转换时会有两套方案:


1.PropertyEditor,这是Spring最初提供的方案,扩展了java中的PropertyEditor(java原先提供这个接口的目的更多是为了进行图形化编程)

2.ConversionService,Spring后来提供的一个进行类型转换的体系,用来取代PropertyEditor,因为PropertyEditor有很大的局限性,只能进行String->Object的转换。

画图如下:

微信图片_20221113110941.png


相关文章
|
6月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
75 0
|
Java Nacos Spring
Nacos spring-cloud 版本没找到共享配置文件的说明,Nacos服务中共享,并且可以被多个应用获取和使用。这个在官网哪里有说明啊
Nacos spring-cloud 版本没找到共享配置文件的说明,Nacos服务中共享,并且可以被多个应用获取和使用。这个在官网哪里有说明啊
73 1
|
5月前
|
存储 Java 程序员
Spring 注册BeanPostProcessor 源码阅读
Spring 注册BeanPostProcessor 源码阅读
|
Java 中间件 Maven
Spring 6 源码编译和高效阅读源码技巧分享
Spring 6 源码编译和高效阅读源码技巧分享
|
Java API Spring
Spring 6 源码编译和高效阅读源码技巧分享
Spring 6 源码编译和高效阅读源码技巧分享
|
Java Spring
Spring 官网无法查看的版本依赖如何查看?
Spring 官网无法查看的版本依赖如何查看?
70 0
|
前端开发 Java Spring
《Spring MVC》 第六章 MVC类型转换器、格式化器
《Spring MVC》 第六章 MVC类型转换器、格式化器
190 0
|
Java 容器 Spring
【Spring源码阅读】IOC容器的依赖注入
SpringIOC容器的依赖注入发生在用户第一次向IOC容器获取Bean时。除在BeanDefinition中设置lazy-init属性让容器完成bean的预实例化。我们在前面《Spring-IOC容器接口设计与功能》中曾讲过,容器BeanFactory通过getBean方法获取Bean。所以这篇文章,我们将从getBean()方法入手分析SpringIOC容器依赖注入的过程。
|
XML Java 数据格式
Spring源码阅读-IOC容器初始化过程
Spring IOC容器的初始化过程:Resource定位,BeanDefinition载入,向IOC容器注册BeanDefinition。整个过程由refresh()方法触发,三个过程由不同的模块完成,使用户更加灵活的对这三个过程剪裁和扩展。
150 0
|
XML 缓存 JSON
Spring MVC 阅读官方文档知识点总结
Spring MVC 阅读官方文档知识点总结
184 8