[spring源码学习]10、IOC源码-conversionService

1、代码示例java

  一、咱们在以前的Person类里新增一个两个属性,分别是客户的兴趣和生日,兴趣爱好有不少,咱们使用list进行保存,生日使用日期进行保存spring

public class Person {
    private String name;
    public Date birth;
    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    //兴趣爱好
    public List<String> interests;


    public List<String> getInterests() {
        return interests;
    }

    public void setInterests(List<String> interests) {
        this.interests = interests;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void sayHello(){
        System.out.println("hello "+this.name);
    }

}

  二、在bean里咱们注入这两个参数缓存

    <bean name="person" class="com.zjl.Person">
        <property name="name" value="zhangsan"></property>
        <property name="interests" value="足球,篮球"></property>
        <property name="birth" value="2015-01-01"></property>
    </bean>

  三、测试代码,咱们打印出zhangsan的兴趣和生日ide

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
        Person person=(Person)context.getBean("person");
        System.out.println(person.interests);
        System.out.println(person.birth);
    }
}

  四、运行结果,很不幸,咱们收到了一个异常信息,提示不能将字符串转为日期格式源码分析

Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property 'birth': no matching editors or conversion strategy found

  五、回看第七章源码部分,咱们在源码的第10部分有以下代码,从系统获取一个conversionService,并将它放入到beanFactory中去,应该是转化,咱们找到conversionService的定义方法:测试

        //查找是否有id为conversionService的bean,若是有,设置进beanFactory
        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));
        }

  六、咱们试着写一个这样的beanui

<bean id="conversionService" class="com.zjl.MyConversionService"></bean>

  七、类的构造以下this

public class MyConversionService implements ConversionService {

    @Override
    public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
        //判断目标类型是不是Date
        if(Date.class.isAssignableFrom(targetType)){
            return true;
        }
        System.out.println(targetType);
        return false;
    }

    @Override
    public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {

        //判断目标类型是不是Date
        if(Date.class.isAssignableFrom(targetType.getObjectType())){
            return true;
        }
//        System.out.println(targetType);
        return false;
    }

    @Override
    public <T> T convert(Object source, Class<T> targetType) {
//        System.out.println("convert");
        return null;
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        //若是源类型是string,咱们直接将他转化为Date类型
        if(String.class.isAssignableFrom(sourceType.getObjectType())){
            DateFormat format=new SimpleDateFormat("yyyy-MM-dd");
            try {
                return format.parse((String) source);
            } catch (ParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
//        System.out.println("convert1");
        return null;
    }

}

  八、打印结果spa

[足球,篮球]
Thu Jan 01 00:00:00 CST 2015

  到这里,例子基本已经完成了,但是仔细观察,咱们会发现其实还有些不完美的地方:rest

  一、咱们写入了足球,篮球,做为两个兴趣,但是程序直接将他变成了一个爱好,也就是list.add("足球,篮球"),与咱们预想不一致。解决的思路咱们能够想象:再注入一个bean,将字符串按照指定字符分割,转为list

  二、因为spirng默认只能读取conversionService,咱们成功转化了字符串为日期,若是想完成第一步的转化就出现了问题,咱们不妨将多个conversion方法注入到bean-conversionService中,而后他依次调用和选择

2、源码分析

  一、咱们看下spring中如何使用conversionService和帮咱们实现一些预制的转化方法的,将咱们本身定义的converter也注入进去

   <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <bean class="com.zjl.MyConverter">
            </bean>
        </property>
    </bean>

   二、咱们初始化bean的时候,跟踪代码到这里,获取了系统注入的conversionService

        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;
                }
            }
        }

  三、到canConvert为在service中获取指定源格式和目标格式的converter,判断是否能够获取

    public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
        Assert.notNull(targetType, "targetType to convert to cannot be null");
        if (sourceType == null) {
            return true;
        }
        GenericConverter converter = getConverter(sourceType, targetType);
        return (converter != null);
    }

  四、在缓存中获取converter,若是没有,到set中获取,保存到缓存中,若是set也没有获取,保存为NO_MATCH

    protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
        ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
        GenericConverter converter = this.converterCache.get(key);
        if (converter != null) {
            return (converter != NO_MATCH ? converter : null);
        }

        converter = this.converters.find(sourceType, targetType);
        if (converter == null) {
            converter = getDefaultConverter(sourceType, targetType);
        }

        if (converter != null) {
            this.converterCache.put(key, converter);
            return converter;
        }

        this.converterCache.put(key, NO_MATCH);
        return null;
    }

  五、找到converter后,调用conversionService.convert(newValue, sourceTypeDesc, typeDescriptor),若是没有converter就直接抛出错误

        GenericConverter converter = getConverter(sourceType, targetType);
        if (converter != null) {
            Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
            return handleResult(sourceType, targetType, result);
        }
        return handleConverterNotFound(source, sourceType, targetType);

  六、调用ConversionUtils.invokeConverter,调用converter的convert的方法

    public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType,
            TypeDescriptor targetType) {
        try {
            return converter.convert(source, sourceType, targetType);
        }
        catch (ConversionFailedException ex) {
            throw ex;
        }
        catch (Exception ex) {
            throw new ConversionFailedException(sourceType, targetType, source, ex);
        }
    }

  七、至于convert方法中如何进行转化就全靠咱们本身写了

3、总结

  对于spring的IOC中注入的参数,虽然都是字符串,可是通过系统提供的接口咱们能够将它与bean中字段的各类类型进行适配,适配过程须要定义conversionService,spring提供了默认的实现FactoryBean,他能够以set形式注入自定义的converter,也使用系统默认的转换器。

  咱们来改造咱们以前的转换器,经过源代码能够看到Converter以泛型中的类型做为是否对这次数据转换的选择  

public class MyConverter implements Converter<String,Date> {

    @Override
    public Date convert(String source) {
        DateFormat format=new SimpleDateFormat("yyyy-MM-dd");
        try {
            return format.parse((String) source);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

}
相关文章
相关标签/搜索