前言
在平時的開發(fā)中,對于Spring為我們提供的數(shù)據(jù)類型轉(zhuǎn)換與校驗功能似乎已經(jīng)使用的習(xí)以為常,但是對于其是如何在Spring框架背后運行的以及它自身的體系結(jié)構(gòu),一直以來我都沒有一個特別清晰的認(rèn)識,因此花了幾天時間將Spring文檔再次精讀了一遍,同時對部分源碼做了分析,故在此將自己的學(xué)習(xí)成果進(jìn)行一個總結(jié)。原本想一篇文章搞定的,發(fā)現(xiàn)如果想細(xì)致分析根本一篇文章搞不定,所以我在這里將其拆分成3文章,本系列文章所分析的內(nèi)容主要包括以下幾個方面:
- PropertyEditor類型轉(zhuǎn)換體系以及源碼分析其背后的工作流程
- Type Conversion、Formatter SPI轉(zhuǎn)換體系的結(jié)構(gòu)以及核心類的源碼分析
- Spring的數(shù)據(jù)校驗的工作機制以及源碼分析校驗的執(zhí)行流程
本篇文章首先對ProeprtyEditor轉(zhuǎn)換體系以及源碼做一個分析,如果分析有不對之處,還望指正錯誤?。?!
Spring類型轉(zhuǎn)換體系
Spring的類型轉(zhuǎn)換系統(tǒng)在經(jīng)歷了版本升級變更之后,也隨之發(fā)生了很大的變化,從3.0前的PropertyEditor體系轉(zhuǎn)變到了3.0后的Converter和Formatter轉(zhuǎn)換體系。這里會先從早期的PropertyEditor開始分析,然后再分析3.0后的類型轉(zhuǎn)換體系;分析ProperyEditor更有助于我們對Spring內(nèi)部實現(xiàn)機制的理解,然后通過不同的類型轉(zhuǎn)換系統(tǒng)進(jìn)行一個對比,理解版本升級所帶來的意義與好處。
1. PropertyEditor類型轉(zhuǎn)換體系分析
首先讓我們對傳統(tǒng)的PropertyEditor類型轉(zhuǎn)換體系做一個復(fù)習(xí)與分析,來看看在一個看似不起眼的數(shù)據(jù)轉(zhuǎn)換操作在其背后到底發(fā)生了什么。
1.1 PropertyEditor
通過java.beans.PropertyEditor其包名我們就可以看出,其本身并非Spring中定義的接口,它其實是Java Bean規(guī)范中所定義的一個接口,其設(shè)計初衷在于是用于完成GUI中的輸入與對象屬性之間的轉(zhuǎn)換操作;Spring只是借用了PropertyEditor這個接口的定義與功能,來完成字符串與對象屬性之間的轉(zhuǎn)換。在Spring 3.0之前,在Spring整個體系中,所有完成字符串與其他數(shù)據(jù)類型轉(zhuǎn)換操作都是由ProperyEditor接口來完成的。
Spring通過PropertyEditor作為其數(shù)據(jù)類型轉(zhuǎn)換的基礎(chǔ)架構(gòu),同時自己還定義了許多默認(rèn)的ProrpertyEditor的實現(xiàn),這些實現(xiàn)類都位于spring-beans這個模塊中的propertyeditors包中。

這些內(nèi)置的PropertyEditor會有部分在默認(rèn)情況下就已經(jīng)被加了到IOC容器中,而有些PropertyEditor在默認(rèn)情況下并沒有自動加入,需要用戶手動進(jìn)行配置,后面我們通過源碼可以看到Spring默認(rèn)所注冊了哪些PropertyEditor。
1.2 PropertyEditorSupport
由于PropertyEditor是一個類型轉(zhuǎn)換的接口,其里面定義了很多與我們實際使用上無關(guān)的方法。如果我們想要使用PropertyEditor的話,我們通常只需要繼承java.beans.PropertyEditorSupport這個類,并且重寫其setAsText(String source)方法即可,通過它將輸入的字符串轉(zhuǎn)換成我們期望的數(shù)據(jù)類型。

通過上圖可以看到,Spring中的所提供的內(nèi)置的
PropertyEditor也都是繼承PropertyEditorSupport來完成類型轉(zhuǎn)換的。
1.3 PropertyEditor的基本使用
下面我們通過一個簡單的例子來復(fù)習(xí)一下PropertyEditor的基本使用。這里為了減少篇幅,我就不給出需要的maven依賴,之后如果需要參考的話可以直接到GitHub下載源碼。
package com.panlingxiao.spring.validation.domain;
import lombok.Data;
/**
* Created by panlingxiao on 2016/5/29.
* 這里使用lombok來取代getter和setter方法。
*/
@Data
public class Circle {
private Point point;
}
package com.panlingxiao.spring.validation.domain;
import lombok.Data;
@Data
public class Point {
int x, y;
}
上面給出兩個非常簡單的類,我們希望完成的是輸入1;2,能夠自動進(jìn)行分割,然后轉(zhuǎn)換成point的x和y屬性。
下面我們自己定義PropertyEditor來完成數(shù)據(jù)的轉(zhuǎn)換:
package com.panlingxiao.spring.validation.editor;
import com.panlingxiao.spring.validation.domain.Point;
import java.beans.PropertyEditorSupport;
/**
* 自定義PropertyEditor,完成String到Point的轉(zhuǎn)換
*/
public class PointEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] splits = text.split(";");
Point point = new Point();
point.setX(Integer.parseInt(splits[0]));
point.setY(Integer.parseInt(splits[1]));
/*
*需要將裝換后的結(jié)果設(shè)置到Editor的value屬性中,因為外部會通過getValue獲取到轉(zhuǎn)換的結(jié)果。
*/
setValue(point);
}
}
在完成ProperyEditor編寫完成后,我們只需將其注冊到IOC容器中就可以自動完成String到Point之間的轉(zhuǎn)換,下面來編寫spring的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通過CustomEditorConfigurer這個BeanFactoryProcessor來完成自定義的ProperyEditor到IOC容器的添加功能 -->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<bean class="com.panlingxiao.spring.validation.editor.MyPropertyEditorRegistrar"/>
</property>
</bean>
<bean class="com.panlingxiao.spring.validation.domain.Circle" id="circle">
<property name="point" value="1;2"/>
</bean>
</beans>
在編寫完成配置文件之后,我們編寫一個測試類來驗證一下配置的結(jié)果是否有效:
package com.panlingxiao.spring.propertyeditor;
import com.panlingxiao.spring.validation.domain.Circle;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by panlingxiao on 2016/6/1.
*/
public class TestPropertyEditor {
@Test
public void testCustomEditorConfigurer() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("prop-editor-app-context.xml");
Circle circle = applicationContext.getBean("circle", Circle.class);
System.out.println(circle.getPoint());
}
}

除了通過CustomEditorConfigurer來完成自定義PropertyEditor的注入,Spring也支持另外一種基于Java Bean規(guī)范的自動查找機制。下面我們也通過一個例子來驗證一下這種方式:
package com.panlingxiao.spring.validation.domain;
import lombok.Data;
/**
* Created by panlingxiao on 2016/5/29.
*/
@Data
public class Boo {
private Foo foo;
}
package com.panlingxiao.spring.validation.domain;
import lombok.Data;
/**
* Created by panlingxiao on 2016/5/29.
*/
@Data
public class Foo {
private int x,y;
}
上面給出兩個類也同樣非常簡單,我們也希望完成的是輸入1;2,能夠自動進(jìn)行分割,然后轉(zhuǎn)換成一個Foo對象的x和y屬性即可,下面我們再看定義自己的PropertyEditor:
package com.panlingxiao.spring.validation.domain;
import java.beans.PropertyEditorSupport;
/**
* Created by panlingxiao on 2016/5/29.
*/
public class FooEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] splits = text.split(";");
Foo foo = new Foo();
foo.setX(Integer.parseInt(splits[0]));
foo.setY(Integer.parseInt(splits[1]));
setValue(foo);
}
}
注意到:自定義的PropertyEditor必須與對應(yīng)的類型在同一個包下,并且其名字必須為xxxEditor,這樣才能實現(xiàn)自動查詢的功能

下面,我們通過一個測試類來簡單的驗證一下該功能是否真的有效,首先編寫一個XML配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.panlingxiao.spring.validation.domain.Boo" id="boo">
<property name="foo" value="1;2"/>
</bean>
</beans>
/**
* 通過約定機制來自動查詢PropertyEditor完成類型轉(zhuǎn)換
*/
@Test
public void testPropertyEditorByConvention(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("prop-convention-app-context.xml");
Boo boo = ctx.getBean("boo", Boo.class);
System.out.println("foo.x: "+boo.getFoo().getX()+",foo.y: "+boo.getFoo().getY());
}

通過上面的運行結(jié)果我們可以看到,我們自己所定義的ProeprtyEditor確實已經(jīng)被Spring的IOC容器所管理,并且成功地完成了類型的轉(zhuǎn)換操作。但是Spring容器在背后具體做了什么,對于我們而言卻完全一無所知,下面我們將繼續(xù)深入學(xué)習(xí)一下,并且從源碼的角度仔細(xì)分析一下Spring如何對PropertyEditor進(jìn)行管理的,如何將字符串轉(zhuǎn)換成我們期望的類型。
1.4 深入分析PropertyEditor類型轉(zhuǎn)換體系以及源碼分析
PropertyEditor類型轉(zhuǎn)換體系牽涉到的類比較多,下面我們對其涉及到接口與類逐一進(jìn)行分析,在了解了核心接口與類之后,再去看Spring中的PropertyEditor類型轉(zhuǎn)換體系就會清晰很多。
首先,我們應(yīng)該所思考的問題是:Spring中的內(nèi)置的PropertEditor與我們定義的ProeprtyEditor是被誰來完成管理的呢?是BeanFactory還是ApplicationContext呢?這樣的問題都不算非常準(zhǔn)確,真正完成管理功能應(yīng)該是PropertyEditorRegistry,下面我們對這個接口做一個比較細(xì)致的分析。
PropertyEditorRegistry
org.springframework.beans.PropertyEditorRegistry接口提供了對PropertyEditor注冊以及查找功能,因此其主要提供是提供了對PropertyEditor的管理功能,首先來看看這個接口的描述:

下面是該接口中所定義的方法:
/**
根據(jù)屬性的類型來指定其對應(yīng)的PropertyEditor
*/
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
/**
* 根據(jù)屬性的類型以及屬性的名字來指定其對應(yīng)的PropertyEditor,該方法并
* 不常用,但是可以做到更細(xì)粒度的轉(zhuǎn)換操作
*/
void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor);
/**
根據(jù)指定的類型以及屬性的名字,查詢其對應(yīng)的ProeprtyEditor,屬性的名字可以為null
*/
PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath);
通過上面的描述,我們應(yīng)該可以明白PropertyEditorRegistry這個接口的作用,下面我們來看看其具體的幾個實現(xiàn)類。
PropertyEditorRegistrySupport
由于PropertyEditorRegistry只是定義對PropertyEditor注冊和查找的方法,其具體的核心實現(xiàn)類是org.springframework.beans.PropertyEditorRegistrySupport,真正對PropertyEditor管理的操作全部在該類中實現(xiàn),下面來看看PropertyEditorRegistrySupport的源碼,由于PropertyEditorRegistrySupport源碼篇幅比較多,這里就采用截圖來說明其實現(xiàn):

通過上面的標(biāo)注我們看到PropertyEditorSupport底層對于不同種類的PropertyEditor使用不同的Map來進(jìn)行存儲,下面我們看下它是如何進(jìn)行注冊的。

其注冊的實現(xiàn)機制也并沒有出人意料的地方,就是判斷存儲Classs與PropertyEditor之間映射關(guān)系的Map是否已經(jīng)存在,如果不存在則先創(chuàng)建一個LinkedHashMap,如果有就直接進(jìn)行存儲映射關(guān)系。
前面我們提到過在IOC容器中默認(rèn)就會內(nèi)置一些PropertyEditor,通過createDefaultEditors()我們可以清楚地看到其默認(rèn)所添加的PropertyEditor。

上面就是PropertyEditorRegistrySupport最核心的實現(xiàn),下面繼續(xù)來看另外一個重要的接口。
PropertyEditorRegistrar
初看PropertyEditorRegistrar時,可能會因為與PropertyEditorRegistry在名字上的相似性而混淆,下面我們看下PropertyEditorRegistrar到底是干嘛的。

從接口的描述上我們可以看到,PropertyEditorRegistrar的作用是將用戶自定義的PropertyEditor注冊到PropertyEditorRegistry中。通過其registerCustomEditor方法中的參數(shù)我們可以看到,其所接受的正是一個PropertyEditorRegistry,通過方法的參數(shù)將用戶自定義的ProepertyEditor加入到PropertyEditorRegistry被其進(jìn)行管理。
PropertyEditorRegistrar對于如果我們希望將一組相同的PropertyEditor應(yīng)用在多個地方時是非常有用的 ( 比如希望將相同的一組PropertyEditor既應(yīng)用在IOC容器中,同時又應(yīng)用在Spring MVC的DataBinder中),此時就可以先定義一個PropertyEditorRegistrar的實現(xiàn)類,來完成通用的ProepertyEditor注冊操作,然后將PropertyEditorRegistrar作為一個ProeprtyEditor的集合設(shè)置到不同的地方,此時就可以做到代碼復(fù)用。
PS:相同的PropertyEditor需要在多處進(jìn)行注冊的原因是因為我們在IOC容器中通過CustomEditorConfigurer添加了自定義的PropertyEditor后,其并不會對SpringMVC中所使用的DataBinder而生效,因此需要再次進(jìn)行注冊,我們通過分析CustomEditorConfigurer可以在其注釋說明中清楚地看到這點說明。

下面我們來看一個PropertyEditorRegistrar的例子,我們還是使用上面的例子作為基礎(chǔ),首先定義一個PropertyEditorRegistrar來完成PropertyEditor的注冊功能:
package com.panlingxiao.spring.validation.editor;
import com.panlingxiao.spring.validation.domain.Point;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
/**
* Created by panlingxiao on 2016/6/2.
*/
public class MyPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
//將自己所定義的PropertyEditor注冊到PropertyEditorRegistry中
registry.registerCustomEditor(Point.class,"point",new PointEditor());
}
}
我們只需修改一下配置文件,現(xiàn)在要注入的不再是一個Map,而是一個PropertyEditorRegistrar。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
通過CustomEditorConfigurer這個BeanFactoryProcessor來完成自定義的ProperyEditor到IOC容器的添加功能
-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<bean class="com.panlingxiao.spring.validation.editor.MyPropertyEditorRegistrar"/>
</property>
<bean class="com.panlingxiao.spring.validation.domain.Circle" id="circle">
<property name="point" value="1;2"/>
</bean>
</beans>
/**
* 測試基于{@link org.springframework.beans.PropertyEditorRegistrar}的PropertyEditor的注冊
*/
@Test
public void testPropertyEditorByPropertyRegistrar(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("prop-registrar-app-context.xml");
Circle circle = ctx.getBean("circle", Circle.class);
Assert.assertEquals(1,circle.getPoint().getX());
Assert.assertEquals(2,circle.getPoint().getY());
}
運行結(jié)果:

通過上面的結(jié)果我們看到了PropertyRegistrar的作用以及基本用法,下面我們再介紹一個比較底層接口,由于該接口和源碼息息相關(guān),因此不得不在此做出說明,它就是BeanWrapper接口:
BeanWrapper
BeanWrapper是Spring中一個比較底層的接口,在通常情況下,我們作為普通用戶是不會涉及到這個接口的使用的,其主要被Spring的內(nèi)部所使用,但是由于我們下面會設(shè)計到IOC部分的源碼分析,故在此也對其做一個說明,對于查看后面的源碼會更有幫助。

從上面的描述我們可以了解到BeanWrapper接口的基本作用。BeanWrapper除了提供了標(biāo)準(zhǔn)的JavaBean的訪問方式以外,同時還繼承了PropertyEditorRegistry,因此BeanWrapper也同樣具有對ProeprtyEditor的管理功能,下面是BeanWrapper的繼承結(jié)構(gòu)圖:

Spring底層其實在創(chuàng)建一個Bean時,IOC容器首先將用戶自定義的PropertyEditor注冊到BeanWrapper中,然后通過BeanWrapper通過注入的PropertyEditor完成數(shù)據(jù)類型的轉(zhuǎn)換,再將轉(zhuǎn)換后的結(jié)果通過反射注入到Bean的屬性中,該結(jié)論可以在調(diào)式后續(xù)的源碼中得以驗證。
下面我們先通過一個簡單的例子來簡單說明一下BeanWrapper的使用:
package com.panlingxiao.spring.validation.domain;
import com.panlingxiao.spring.validation.annotation.MyDate;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.Id;
import java.util.Date;
import java.util.List;
/**
* Created by panlingxiao on 2016/5/26.
*/
@Data
public class Person {
@Id
private Integer id;
private String name;
private int age;
private Date birthday;
private List<Integer> nums;
private Boo boo;
private Point point;
}
/**
* 使用BeanWrapperImpl設(shè)置基本屬性
*/
@Test
public void testSetBasicPropertyValue(){
Person person = new Person();
BeanWrapperImpl wrapper = new BeanWrapperImpl(person);
wrapper.setPropertyValue("id", "1");
Assert.assertEquals(1, person.getId().intValue());
//設(shè)置BeanWrapper自動創(chuàng)建內(nèi)聯(lián)屬性,否則會引發(fā)NullValueInNestedPathException
wrapper.setAutoGrowNestedPaths(true);
wrapper.setPropertyValue("nums[0]", "123");
wrapper.setPropertyValue("nums[1]", "456");
System.out.println(person.getNums());
wrapper.setPropertyValue("boo.foo.x", "1");
System.out.println(person.getBoo().getFoo().getX());
//注冊自定義的PropertyEditor到BeanWrapper中
wrapper.registerCustomEditor(Point.class,new PointEditor());
//BeanWrapper通過注冊的PropertyEditor完成數(shù)據(jù)類型的轉(zhuǎn)換
wrapper.setPropertyValue("point","1;2");
}
運行結(jié)果:

至此,對于PropertyEditor體系中牽涉到幾個重要概念就介紹完了,下面我們需要具體來看看我們自己定義的PropertyEditor是如何被添加到IOC容器中,又是如何被IOC添加到BeanWrapper中的,最終BeanWrapper又是如何完成數(shù)據(jù)類型的轉(zhuǎn)換的。
1.首先我們看看CustomEditorConfigurer是如何自動被IOC容器所加載然后調(diào)用的。

我們可以看到CustomEditorConfigurer實現(xiàn)了BeanFactoryPostProcessor接口,BeanFactoryPostProcessor是Spring提供給用戶用于動態(tài)修改application context信息的接口,對于application context它會自動去查詢所有的BeanFactoryPostProcessor,并且調(diào)用其postProcessBeanFactory方法來完成處理,需要注意的是,該方法的調(diào)用是在所有單例Bean初始化前調(diào)用的。
其具體代碼體現(xiàn)為:

通過上面的代碼可以看到,AbstractApplicationContext的refresh()方法會自動完成BeanFactoryPostProcessor的查找和調(diào)用,下面我們具體看看它的具體處理流程,由于invokeBeanFactoryPostProcessors中的方法代碼過多,這里我只截取核心的處理代碼:



可以看到,它會自動去BeanFactory根據(jù)類型獲取到BeanFactoryPostProcessor的名字,然后根據(jù)名字查詢到具體的實例,最后調(diào)用其定義postProcessBeanFactory完成用戶自己的處理。
通過上面的分析,我們應(yīng)該能夠清楚地看到一個BeanFactoryPostProcesor是如何被ApplicationContext所處理的,而我們所配置的CustomEditorConfigurer正是一個BeanFactoryPostProcesor。
2.下面我們來看看CustomEditorConfigurer的postProcessBeanFactory具體實現(xiàn),分析我們所定義的PropertyEditor是如何被IOC容器所使用:
@SuppressWarnings("unchecked")
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
/*
* 將用戶所定義的PropertyEditorRegistrar注入到BeanFactory中,
* BeanFactory底層會通過BeanWrapper來創(chuàng)建Bean,此時BeanFactory就會將其所管理的PropertyEditor傳遞給
* BeanWrapper底層又維護(hù)著一個TypeConverterDelegate,通過TypeConverterDelegate來真正完成真正的類型轉(zhuǎn)換
* 再將轉(zhuǎn)換后結(jié)果通過BeanWrapper使用反射注入到屬性中。
*/
if (this.propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar);
}
}
//迭代存儲Class與PropertyEditor映射關(guān)系的map
if (this.customEditors != null) {
for (Map.Entry<String, ?> entry : this.customEditors.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Class requiredType = null;
try {
//通過反射獲取具體要轉(zhuǎn)換的Java類型
requiredType = ClassUtils.forName(key, this.beanClassLoader);
//判斷value的具體類型,可能是一個PropertyEditor、也可能是一個Class、也可能是一個String
//如果設(shè)置的是一個具體的PropertyEditor,那么該被PropertyEditor會被共享使用,每次使用都會被加鎖來處理,因此建議不要
//將PropertyEditor設(shè)置為共享,從而影響性能
if (value instanceof PropertyEditor) {
if (logger.isWarnEnabled()) {
logger.warn("Passing PropertyEditor instances into CustomEditorConfigurer is deprecated: " +
"use PropertyEditorRegistrars or PropertyEditor class names instead. " +
"Offending key [" + key + "; offending editor instance: " + value);
}
beanFactory.addPropertyEditorRegistrar(
new SharedPropertyEditorRegistrar(requiredType, (PropertyEditor) value));
}
//當(dāng)設(shè)置的是一個Class,則直接存儲轉(zhuǎn)換類型與PropertyEditor的類型
else if (value instanceof Class) {
beanFactory.registerCustomEditor(requiredType, (Class) value);
}
//當(dāng)設(shè)置的是一個String,則先反射出PropertyEditor,再存儲轉(zhuǎn)換類型與PropertyEditor的類型
else if (value instanceof String) {
Class editorClass = ClassUtils.forName((String) value, this.beanClassLoader);
Assert.isAssignable(PropertyEditor.class, editorClass);
beanFactory.registerCustomEditor(requiredType, editorClass);
}
else {
throw new IllegalArgumentException("Mapped value [" + value + "] for custom editor key [" +
key + "] is not of required type [" + PropertyEditor.class.getName() +
"] or a corresponding Class or String value indicating a PropertyEditor implementation");
}
}
catch (ClassNotFoundException ex) {
if (this.ignoreUnresolvableEditors) {
logger.info("Skipping editor [" + value + "] for required type [" + key + "]: " +
(requiredType != null ? "editor" : "required type") + " class not found.");
}
else {
throw new FatalBeanException(
(requiredType != null ? "Editor" : "Required type") + " class not found", ex);
}
}
}
}
}
3.看完自定義的PropertyEditor如何被加入到BeanFactory中之后,下面我們繼續(xù)看BeanFactory是在何時創(chuàng)建一個我們所定義的Bean,并且在創(chuàng)建Bean后如何通過BeanWrapper將PropertyEditor完成類型轉(zhuǎn)換。


這里為了減少篇幅,我直接給出代碼的截圖,通過上面的的方法名我們可以看到,ApplicationContext會預(yù)先完成單例的Bean的初始化操作,下面我們看看它是如何預(yù)初始化單例的Bean的,由于這里的代碼比較深,我們直接跳到我們所最關(guān)注的部分,即IOC如何使用BeanWrapper部分。

通過上圖我們看到了一個initBeanWrapper方法,該方法就是IOC容器將用戶所配置的PropertyEditor注冊到BeanWrapper的過程,下面我們繼續(xù)查看該方法具體做了什么。



看到BeanFactory將PropertyEditor往BeanWrapper注冊之后,下面就應(yīng)該完成Bean的屬性的注入操作。

populate方法是完成Bean的屬性注入操作,通過上面的截圖看到,在未調(diào)用該方法之前,此時對象的屬性還是null,下面是其如何完成數(shù)據(jù)類型的轉(zhuǎn)換過程:


在通過TypeConverterDelegate完成屬性類型轉(zhuǎn)換后,最后再通過BeanWrapper將屬性注入到對象中。

至此,PropertyEditor的體系結(jié)構(gòu)以及其如何在IOC容器中的過程已經(jīng)分析完了。這里需要說明的一下,由于Spring的源碼過多,我曾嘗試想在博客中進(jìn)行逐行分析,但是發(fā)現(xiàn)根本不可能,所以上面的分析也只能以截圖的形式給出,對于具體的細(xì)節(jié)如果想更進(jìn)一步深入,就需要自己再進(jìn)一步看源碼和調(diào)式。說了這么多關(guān)于PropertyEditor的事,我們最后所要思考的是PropertyEditor其所存在的缺點。
PropertyEditor的缺點分析
1.只能完成字符串到Java類型的轉(zhuǎn)換,并不能完成任意類型之間的轉(zhuǎn)換。
2.由于PropertyEditor是非線程安全,因此對于每一次的類型轉(zhuǎn)換,都需要創(chuàng)建一個新的PropertyEdtitor,如果希望達(dá)到共享,那么底層會使用synchronized來對其進(jìn)行并發(fā)地控制。

由于PropertyEditor存在著這些問題,因此Spring3.0后推出了新的類型轉(zhuǎn)換系統(tǒng),下一節(jié)將繼續(xù)對Conversion Service的體系結(jié)構(gòu)以及部分源碼進(jìn)行分析。
PS:用心寫作真心不易,如果您覺得本文對您有幫助,請點個贊,這是對我最大的鼓勵,謝謝!