SpringMVC 原始碼深度解析BeanWrapper及其實現
一、 BeanWrapper
BeanWrapper是對Bean的包裝,其介面中所定義的功能很簡單包括設定獲取被包裝的物件,獲取被包裝bean的屬性描述器,由於BeanWrapper介面是PropertyAccessor的子介面,因此其也可以設定以及訪問被包裝物件的屬性值。BeanWrapper大部分情況下是在spring ioc內部進行使用,通過BeanWrapper,spring ioc容器可以用統一的方式來訪問bean的屬性。使用者很少需要直接使用BeanWrapper進行程式設計。
二、 BeanWrapperImpl
BeanWrapperImpl類是對BeanWrapper介面的預設實現,它包裝了一個bean物件,快取了bean的內省結果,並可以訪問bean的屬性、設定bean的屬性值。BeanWrapperImpl類提供了許多預設屬性編輯器,支援多種不同型別的型別轉換,可以將陣列、集合型別的屬性轉換成指定特殊型別的陣列或集合。使用者也可以註冊自定義的屬性編輯器在BeanWrapperImpl中。
三、BeanWrapper類圖
四、BeanWrapperImpl設定所包裝的bean屬性值序列圖
五、 BeanWrapperImpl構造方法原始碼分析
5.1 BeanWrapperImpl構造方法
BeanWrapperImpl類有多個過載方法,下面的構造方法傳入一個Object物件,此物件就是被BeanWrapperImpl類所包裝的bean
[java] view plaincopyprint?- public BeanWrapperImpl(Object object) {
-
registerDefaultEditors();
- setWrappedInstance(object);
- }
構造方法的實現很簡單,第一步在registerDefaultEditors()方法內部設定屬性defaultEditorsActive值為true
第二步則呼叫setWrappedInstance(object)方法,進行初始化以及設定被包裝的物件
5.2 setWrappedInstance(object)方法
此方法內部對BeanWrapperImpl類的一些重要屬性進行了初始化,並建立了TypeConverterDelegate類的例項作為型別轉換處理物件。在此之後,將對被包裝bean
5.3 CachedIntrospectionResults類
CachedIntrospectionResults類用於對物件的Class進行內省分析,儲存物件的PropertyDescriptor資訊,其靜態方法
static CachedIntrospectionResults forClass(Class beanClass)
throws BeansException
在BeanWrapperImpl類中被呼叫,用於對BeanWrapperImpl類所包裝的bean進行內省分析,並返回內省分析結果給BeanWrapperImpl
六、 BeanWrapperImpl設定屬性值原始碼分析
6.1 setPropertyValue(PropertyValue pv)方法
BeanWrapperImpl類有多個設定bean屬性值的過載方法,此處以
publicvoid setPropertyValue(PropertyValue pv)
throws BeansException
方法作為說明。
[java] view plaincopyprint?- publicvoid setPropertyValue(PropertyValue pv) throws BeansException {
- PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
- if (tokens == null) {
- ……………..
- BeanWrapperImpl nestedBw = null;
- try {
- //根據屬性名獲取BeanWrapImpl物件,支援多重屬性的遞迴分析處理
- nestedBw = getBeanWrapperForPropertyPath(propertyName);
- }
- catch ………..
- tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
- //如果nestedBw等於this,則設定resolvedTokens屬性值為tokens
- if (nestedBw == this) {
- pv.getOriginalPropertyValue().resolvedTokens = tokens;
- }
- nestedBw.setPropertyValue(tokens, pv);
- }
- else {
- setPropertyValue(tokens, pv);
- }
- }
上述方法根據tokens變數是否為null,有兩個不同的分支。其中當tokens為null時,則會對屬性名進行遞迴呼叫分析處理,返回分析處理後的BeanWrapImpl物件nestedBw。如果nestedBw==this,則會設定pv的resolvedTokens屬性值,最後將呼叫nestedBw物件的設定屬性值方法設定屬性
6.2 getBeanWrapperForPropertyPath方法
getBeanWrapperForPropertyPath方法用於對屬性名稱(包括多重屬性)進行處理,並返回BeanWrapperImpl物件。所支援的屬性名稱包括:多重屬性(以.分隔)、陣列集合map key屬性(以[]方式)。
[java] view plaincopyprint?- protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) {
- //根據屬性路徑獲取其第一個屬性分隔符.的下標
- int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
- //如果找到,則表示是有多重屬性遞迴處理
- // Handle nested properties recursively.
- if (pos > -1) {
- //獲取第一個屬性分隔符前面的屬性名稱
- String nestedProperty = propertyPath.substring(0, pos);
- //獲取第一個屬性分隔符後面的字串
- String nestedPath = propertyPath.substring(pos + 1);
- //獲取第一個屬性片斷的BeanWrapperImpl物件nestedBw
- BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
- //呼叫nestedBw的getBeanWrapperForPropertyPath方法,對第一個屬性分隔符後面的屬性字串進行處理
- return nestedBw.getBeanWrapperForPropertyPath(nestedPath);
- }
- //否則,返回this物件本身
- else {
- returnthis;
- }
- }
在此方法中對巢狀型別的屬性進行了分析,首先將處理第一個.前的屬性,獲取nestedBw物件。然後再呼叫nestedBw物件的此方法遞迴處理第一個.後的其它屬性,並返回處理結果。
getNestedBeanWrapper(nestedProperty)方法則根據nestedProperty獲取巢狀的BeanWrapperImpl物件。
6.3 getPropertyNameTokens方法
getPropertyNameTokens方法內部用於對屬性名全路徑中最後一個.後的屬性名稱分析,返回PropertyTokenHolder物件。
6.4 setPropertyValue(tokens, pv)方法
最終的設定屬性的操作在此方法內部進行實現,此方法將最原始的屬性值經過陣列、集合型別屬性的處理和型別轉換後得到轉換後值convertedValue,並從內省資訊中獲取操作此屬性的方法writeMethod,用反射呼叫writeMethod將引數值convertedValue寫入至被包裝物件的目標屬性中。至此BeanWrapperImpl的物件設值操作處理完成。
七、 BeanWrapperImpl對巢狀屬性的支援
BeanWrapperImpl類通過其成員屬性提供了一種支援巢狀屬性的資料結構,下面是BeanWrapperImpl類的成員:
屬性型別及名稱 |
說明 |
Object object |
被BeanWrapper包裝的物件 |
String nestedPath |
當前BeanWrapper物件所屬巢狀層次的屬性名,最頂層的BeanWrapper此屬性的值為空 |
Object rootObject |
最頂層BeanWrapper所包裝的物件 |
Map nestedBeanWrappers |
快取當前BeanWrapper的下一層屬性的nestedPath和對應的BeanWrapperImpl物件,此BeanWrapperImpl所包裝的對應是屬性nestedPath的值 |
例如有類:
Class Employee
{
Company company;
Card card;
String name;
String id;
get/set方法
}
Class Company
{
String companyName;
Map<String,String> attrs;
get/set方法
}
Class Card
{
………….
}
巢狀屬性:employee.company.attrs[“location”]
最頂層的BeanWrapper是employeeBeanWrapper,其包裝的物件是employeeObj,nestedPath為空,rootObject也是employObj, employeeBeanWrapper裡的nestedBeanWrappers則將包含以下的鍵值對:
“company”---àcompanyBeanWrapper
“card” ---àcardBeanWrapper
對於物件companyBeanWrapper,則其包裝的物件則是companyObj,其nestedPath的值為company,rootObject為employeeObj, nestedBeanWrappers裡面的值為空。
BeanWrapperImpl在呼叫方法
protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath)
分析employee.company.attrs[“location”]屬性值時,將先處理第一個點之前的屬性employee得到頂層的BeanWrapper nestedBw,再呼叫nestedBw. getBeanWrapperForPropertyPath()方法遞迴處理第一個點後的其它屬性,並最終返回所有屬性段都分析後所產生的BeanWrapperImpl物件。
屬性型別及名稱 |
說明 |
String canonicalName |
canonicalName為actualName再加上[key1][key2][key3]..這種形式 儲存當前級別屬性的實際名稱,為[前的字串 如屬性employee.company.attrs[“location”] 在EmployeeBeanWrapperImpl類代表company屬性,則canonicalName值為”company” 如在CompanyBeanWrapperImpl類代表attrs中key為location的屬性,則其canonicalName為attrs[“location”] |
String actualName |
儲存當前級別屬性的實際名稱,為[前的字串 如屬性employee.company.attrs[“location”] 在EmployeeBeanWrapperImpl類代表company屬性,則actualName值為”company” 如在CompanyBeanWrapperImpl類代表attrs中key為location的屬性,則其actualName為”attrs”,取當前屬性級別中”[“前面的字串 |
String[] keys |
代表當前級別屬性中所有位於[與]間的key或索引所組成的陣列 |
注:“當前級別屬性”表示所要訪問在屬性在屬性全路徑中位於..之間的值
九、 所使用到的工具類
在此過程中所使用到的一些工具類和主要方法如下:
org.springframework.beans.PropertyAccessorUtils 用於分析屬性名稱的工具類,提供分析巢狀屬性、集合屬性(包括.[]等)的一些工具方法。
出自於http://blog.csdn.net/zhiweianran/article/details/7919129