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