spring类型转换器(四)

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));//在这个过程中也可进行类型转换
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352