一、代码示例

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

  

[spring源码学习]十、IOC源码-conversionService的更多相关文章

  1. Spring Ioc源码分析系列--Ioc源码入口分析

    Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...

  2. spring cloud深入学习(四)-----eureka源码解析、ribbon解析、声明式调用feign

    基本概念 1.Registe 一一服务注册当eureka Client向Eureka Server注册时,Eureka Client提供自身的元数据,比如IP地址.端口.运行状况指标的Uri.主页地址 ...

  3. 【 js 基础 】【 源码学习 】backbone 源码阅读(一)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  4. 【 js 基础 】【 源码学习 】backbone 源码阅读(二)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 ...

  5. 【 js 基础 】【 源码学习 】backbone 源码阅读(三)浅谈 REST 和 CRUD

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  6. go 源码学习之---Tail 源码分析

    已经有两个月没有写博客了,也有好几个月没有看go相关的内容了,由于工作原因最近在做java以及大数据相关的内容,导致最近工作较忙,博客停止了更新,正好想捡起之前go的东西,所以找了一个源码学习 这个也 ...

  7. 【 js 基础 】【 源码学习 】backbone 源码阅读(三)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  8. Jetty源码学习-编译Jetty源码二三事

    工作小几个月了,JDK基础和web应用框架学的的差不多了,开始学习Jetty源码,费了小半天才编译成功,把自己拆过的坑记录下来. 编译前的环境: MAVEN 3.3.Eclips eLuna Serv ...

  9. Spring源码学习之IOC容器实现原理(一)-DefaultListableBeanFactory

    从这个继承体系结构图来看,我们可以发现DefaultListableBeanFactory是第一个非抽象类,非接口类.实际IOC容器.所以这篇博客以DefaultListableBeanFactory ...

随机推荐

  1. windows命令

    开始--运行--cmd 进入命令提示符 输入netstat -ano 即可看到所有连接的PID 之后在任务管理器中找到这个PID所对应的程序如果任务管理器中没有PID这一项,可以在任务管理器中选&qu ...

  2. HTML+CSS布局技巧及兼容问题【阅读季】

    在IE6和IE7中,行高值必须大于字体的2px以上才能保证字体的完整显示或当作为链接时能显示下划线. IE6 下去掉 input等元素 的边框 border: 0 none; 所有浏览器都可以了 边框 ...

  3. HDU 3791二叉搜索树解题(解题报告)

    1.题目地址: http://acm.hdu.edu.cn/showproblem.php?pid=3791 2.参考解题 http://blog.csdn.net/u013447865/articl ...

  4. Jquery制作--焦点图淡出淡入

    之前写了一个焦点图左右轮播的,感觉淡出淡入用得也比较多,就干脆一起放上来啦.这个容器用了百分比宽度,图片始终保持居中处理,定宽或者自适应宽度都是可以的. 兼容到IE6+以上浏览器,有淡出淡入速度和切换 ...

  5. 修改html页面文字选中样式

    ::selection { background-color: #31B0D5; color: #fff; text-shadow: 0 1px 0 rgba(0,0,0,.2); }

  6. JS学习:第二周——NO.1回调函数

    [回调函数] 定义:把一个函数的定义阶段,作为参数,传给另一个函数: 回调函数调用次数,取决于条件: 回调函数可以传参: 回调函数可以给变this指向,默认是window: 回调函数没有返回值,for ...

  7. springmvc.xml的基本配置

    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w ...

  8. STL~heap

    1.定义 堆:若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树 树中任一非叶子结点的关键字均不大于(或不小于)其子结点的关键字.分为大根数(默认 ...

  9. Activity系列讲解---数据传递

    在Android中,不同的Activity实例可能运行在一个进程中,也可能运行在不同的进程中.因此需要一种特别的机制帮助我们在Activity之间传递消息.Android中通过Intent对象来表示一 ...

  10. Asp.Net 数据库连接字符串

    <configuration> <connectionStrings> <add name="DefaultConnection" providerN ...