spring类型转换器(四)

2020-08-31  本文已影响0人  立志java

spring类型转换器(四)

在spring实例化和spring mvc中涉及到了大量的属性注入。这个过程中不可避免的就是类型转换,这章将会详细说明spring中类型转换器的使用和源码。

使用

PropertyEditor

首先来看下如何在spring中注册自定义的 PropertyEditor

public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(Date.class,new DatePropertyEditor());
    }
}
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="java.util.Date.class" value="cn.zlz.editors.DatePropertyEditor"/>
        </map>
    </property>
    <property name="propertyEditorRegistrars">
        <list>
            <ref bean="customPropertyEditorRegistrar"/>
        </list>
    </property>
</bean>
<bean id="customPropertyEditorRegistrar" class="cn.zlz.editors.CustomPropertyEditorRegistrar"/>

spring内部提供了一个 CustomEditorConfigurer 类,用于用户注册自定义类型转换器,可以通过customEditors注入自定义的PropertyEditor。也可以通过PropertyEditorRegistrar进行注册

PropertyEditor的注册是每个对象都创建一遍类型转换器,使用customEditors注册的是转换器的类型,然后每次都反射转换器对象。由于这样创建不够灵活,所以提供了PropertyEditorRegistrar接口,然后实现registerCustomEditors方法,通过这个方法可以灵活的注册自定义的类型转换器。

ConversionService

<bean id="conversionService"
    class="org.springframework.context.support.ConversionServiceFactoryBean"/>

<bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="example.MyCustomConverter"/>
        </set>
    </property>
</bean>

FormattingConversionService

<bean id="conversionService"    class="org.springframework.format.support.FormattingConversionServiceFactoryBean"/>

源码

ConversionService类型转换实现

ConversionServiceFactoryBean实现了FactoryBean接口,主要就是创建了DefaultConversionService对象并注入的spring容器中

public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
   private Set<?> converters;
   private GenericConversionService conversionService;
   @Override
   public void afterPropertiesSet() {
      this.conversionService = createConversionService();
       //注册自定义类型转换器
      ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
   }
   protected GenericConversionService createConversionService() {
      return new DefaultConversionService();
   }
   @Override
   public ConversionService getObject() {
      return this.conversionService;
   }
}

FormattingConversionServiceFactoryBean同样实现了FactoryBean接口,创建了DefaultFormattingConversionService对象注册到spring容器中。

public class FormattingConversionServiceFactoryBean
    implements FactoryBean<FormattingConversionService>, EmbeddedValueResolverAware, InitializingBean {

    private Set<?> converters;

    private Set<?> formatters;

    private Set<FormatterRegistrar> formatterRegistrars;

    private boolean registerDefaultFormatters = true;

    private StringValueResolver embeddedValueResolver;

    private FormattingConversionService conversionService;
    @Override
    public void afterPropertiesSet() {
        //创建一个DefaultFormattingConversionService对象
        this.conversionService = new DefaultFormattingConversionService(this.embeddedValueResolver, this.registerDefaultFormatters);
        //注册自定义的converters
        ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
        registerFormatters();//注册自定义的formatters
    }

    private void registerFormatters() {
        if (this.formatters != null) {
            for (Object formatter : this.formatters) {
                if (formatter instanceof Formatter<?>) {
                    this.conversionService.addFormatter((Formatter<?>) formatter);
                } else if (formatter instanceof AnnotationFormatterFactory<?>) {
                  this.conversionService.addFormatterForFieldAnnotation((AnnotationFormatterFactory<?>) formatter);
                } else {
                    throw new IllegalArgumentException("");
                }
            }
        }
        if (this.formatterRegistrars != null) {
            for (FormatterRegistrar registrar : this.formatterRegistrars) {
                registrar.registerFormatters(this.conversionService);
            }
        }
    }
}

通过 ConversionServiceFactoryBean或者FormattingConversionServiceFactoryBean都是向spring中注册一个id为conversionService的对象,那么是如何使用这个对象进行类型转换的呢?首先来看ApplicationContext的refresh方法

在refresh方法流程中有这么一段代码

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

可以看到,在ApplicationContext初始化的时候,会查询id为conversionService的ConversionService对象,然后调用BeanFactory的setConversionService方法,设置spring容器的类型转换器。BeanFactory只能配置一个ConversionService,通过上文我们知道DefaultFormattingConversionService是对DefaultConversionService的完全扩展,所以当需要格式化的时候直接使用FormattingConversionServiceFactoryBean就可以。

PropertyEditor实现

在使用ApplicationContext的时候,spring并没有暴露BeanFactory给我们,但是提供了一个BeanFactoryPostProcessor接口,spring实例化bean之前应用所有注册的BeanFactoryPostProcessor的实现类,调用每个的postProcessBeanFactory方法完成对spring容器的扩展。

来看CustomEditorConfigurer的源码,该类实现了BeanFactoryPostProcessor和Ordered接口。主要目的是在postProcessBeanFactory方法中向beanFactory中注册自定义的类型转换器

public class CustomEditorConfigurer implements BeanFactoryPostProcessor, Ordered {

   private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered
   private PropertyEditorRegistrar[] propertyEditorRegistrars;
   private Map<Class<?>, Class<? extends PropertyEditor>> customEditors;

   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
       //向BeanFactory中注册propertyEditorRegistrars
      if (this.propertyEditorRegistrars != null) {
         for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
            beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar);
         }
      }//向BeanFactory中注册customEditors
      if (this.customEditors != null) {
         for (Map.Entry<Class<?>, Class<? extends PropertyEditor>> entry : this.customEditors.entrySet()) {
            Class<?> requiredType = entry.getKey();
            Class<? extends PropertyEditor> propertyEditorClass = entry.getValue();
            beanFactory.registerCustomEditor(requiredType, propertyEditorClass);
         }
      }
   }
}

在BeanFactory中注册全局的类型转换器,实例化每个bean的时候都会根据BeanFactory中注册的类型转换器在BeanWrapper中创建一份类型转换器。类型转换的过程是在getBean的时候,所以最好是在调用getBean即实例化Bean之前向BeanFactory中注册自定义的类型转换器。所以这里使用BeanFactoryPostProcessor实现了自定义类型转换器的注入

Bean实例化的属性注入

接下来我们来看一下bean实例化过程的属性注入是怎么实现的

调用BeanFactory的getBean方法会实例化Bean,第一步首先会反射创建一个目标对象,然后使用BeanWrapperImpll封装实例bean

//反射创建对象
Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
BeanWrapper bw = new BeanWrapperImpl(beanInstance);//使用BeanWrapper包装
this.beanFactory.initBeanWrapper(bw);//将BeanFactory中的类型转换器注册到BeanWrapper中

在initBeanWrapper方法将BeanFactory中定义的全局类型转换器注册到每个BeanWraper中,BeanWraper实现了PropertyEditorRegistry接口,本身带有注册转换器功能。

protected void initBeanWrapper(BeanWrapper bw) {
    //将BeanFactory中的conversionService 添加到BeanWrapper中
   bw.setConversionService(getConversionService());
   registerCustomEditors(bw);
}

将BeanFactory中注册的 propertyEditorRegistrars和customEditors中的类型转换器添加BeanWrapper中。BeanWrapper实现了PropertyEditorRegistry接口

protected void registerCustomEditors(PropertyEditorRegistry registry) {
   PropertyEditorRegistrySupport registrySupport =
         (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
   if (registrySupport != null) {
      registrySupport.useConfigValueEditors();
   }//注册 propertyEditorRegistrars 中注册的类型转换器
   if (!this.propertyEditorRegistrars.isEmpty()) {
      for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
         try {
            registrar.registerCustomEditors(registry);
         } catch (BeanCreationException ex) {
           //...
         }
      }
   }//注册 customEditors 添加的类型转换器
   if (!this.customEditors.isEmpty()) {
      for (Map.Entry<Class<?>, Class<? extends PropertyEditor>> entry : this.customEditors.entrySet()) {
         Class<?> requiredType = entry.getKey();
         Class<? extends PropertyEditor> editorClass = entry.getValue();
         registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass));
      }
   }
}

创建完BeanWrapperImpl并完成类型转换器的注册后,接下来我们直接来看对Bean对象的属性赋值代码。在设置值的时候会首先获取类型转换器,如果没有设置TypeConverter,那么类型转化的功能就由BeanWrapper来实现。

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    //.....
    TypeConverter converter = getCustomTypeConverter();//获取BeanFactory中的 typeConverter
    if (converter == null) {//如果没有配置 使用BeanWrapper作为类型转换器
        converter = bw;
    }
}

循环BeanDefinition中定义的属性值,然后将值转换为目标类型

BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
for (PropertyValue pv : original) {
    //....
    String propertyName = pv.getName();
    Object originalValue = pv.getValue();
    //解析el表达式 beanExpressionResolver,如果指定了属性的type直接在这转换
    Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
    Object convertedValue = resolvedValue;
    boolean convertible = bw.isWritableProperty(propertyName) &&
        !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
    if (convertible) {//进行类型转换
        convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
    }
}

调用BeanWrapper中的convertForProperty将属性值的类型转换为bean中属性的类型,这里会使用到TypeConverter和ConverterService进行类型转换

private Object convertForProperty(Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
    if (converter instanceof BeanWrapperImpl) {
        return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
    } else { //如果有指定的的TyepConverter使用自定义的
        PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
        MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
        return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
    }
}

完成类型转换后,将属性值包装了MutablePropertyValues 类型,BeanWrapperImpl提供了属性的访问功能,调用BeanWrapperImpl的setPropertyValues对bean中的属性值进行设置,至此就完成了bean的属性注入

bw.setPropertyValues(new MutablePropertyValues(deepCopy));//在这个过程中也可进行类型转换
上一篇下一篇

猜你喜欢

热点阅读