• [spring源码学习]十、IOC源码-conversionService


    一、代码示例

      1、我们在之前的Person类里新增一个两个属性,分别是客户的兴趣和生日,兴趣爱好有很多,我们使用list进行保存,生日使用日期进行保存

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

      2、在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>

      3、测试代码,我们打印出zhangsan的兴趣和生日

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

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

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

      5、回看第七章源码部分,我们在源码的第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));
            }

      6、我们试着写一个这样的bean

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

      7、类的构造如下

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

      8、打印结果

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

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

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

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

    二、源码分析

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

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

       2、我们初始化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;
                    }
                }
            }

      3、到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);
        }

      4、在缓存中获取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;
        }

      5、找到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);

      6、调用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);
            }
        }

      7、至于convert方法中如何进行转化就全靠我们自己写了

    三、总结

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

      

  • 相关阅读:
    工业互联网网络安全渗透测试技术研究
    ios加固,ios代码混淆,ios代码混淆工具, iOS源码混淆使用说明详解
    Java代码加密,Java加密方式,Java加密使用说明
    移动App安全等级保护建议
    Android APP安全问题应对办法的探讨
    工业互联网环境下的工业控制系统安全防护
    保护IoT设备安全的5种方法
    移动App安全等级保护测评防护要点
    Windows下给IDApro 安装yara-python 和findcrypt
    gradle-下载地址
  • 原文地址:https://www.cnblogs.com/jyyzzjl/p/5478620.html
Copyright © 2020-2023  润新知