1. 程式人生 > >SpringMVC 原始碼深度解析BeanWrapper及其實現

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?
  1. public BeanWrapperImpl(Object object) {  
  2.        registerDefaultEditors();  
  3.        setWrappedInstance(object);  
  4.     }  

構造方法的實現很簡單,第一步在registerDefaultEditors()方法內部設定屬性defaultEditorsActive值為true

第二步則呼叫setWrappedInstance(object)方法,進行初始化以及設定被包裝的物件

5.2    setWrappedInstance(object)方法

此方法內部對BeanWrapperImpl類的一些重要屬性進行了初始化,並建立了TypeConverterDelegate類的例項作為型別轉換處理物件。在此之後,將對被包裝bean

進行內省分析,內省分析結果儲存在cachedIntrospectionResults屬性中,此屬性是CachedIntrospectionResults類的例項.


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?
  1. publicvoid setPropertyValue(PropertyValue pv) throws BeansException {  
  2.        PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;  
  3.        if (tokens == null) {  
  4.            ……………..  
  5.             BeanWrapperImpl nestedBw = null;  
  6.            try {  
  7.               //根據屬性名獲取BeanWrapImpl物件,支援多重屬性的遞迴分析處理
  8.               nestedBw = getBeanWrapperForPropertyPath(propertyName);  
  9.            }  
  10.            catch ………..  
  11.            tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));  
  12.            //如果nestedBw等於this,則設定resolvedTokens屬性值為tokens
  13.            if (nestedBw == this) {  
  14.               pv.getOriginalPropertyValue().resolvedTokens = tokens;  
  15.            }  
  16.            nestedBw.setPropertyValue(tokens, pv);  
  17.        }  
  18.        else {  
  19.            setPropertyValue(tokens, pv);  
  20.        }  
  21.     }  

上述方法根據tokens變數是否為null,有兩個不同的分支。其中當tokensnull時,則會對屬性名進行遞迴呼叫分析處理,返回分析處理後的BeanWrapImpl物件nestedBw。如果nestedBw==this,則會設定pvresolvedTokens屬性值,最後將呼叫nestedBw物件的設定屬性值方法設定屬性

6.2                   getBeanWrapperForPropertyPath方法

getBeanWrapperForPropertyPath方法用於對屬性名稱(包括多重屬性)進行處理,並返回BeanWrapperImpl物件。所支援的屬性名稱包括:多重屬性(.分隔)、陣列集合map key屬性([]方式)

[java] view plaincopyprint?
  1. protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) {  
  2.        //根據屬性路徑獲取其第一個屬性分隔符.的下標
  3.        int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);  
  4.        //如果找到,則表示是有多重屬性遞迴處理
  5.        // Handle nested properties recursively.
  6.        if (pos > -1) {  
  7.            //獲取第一個屬性分隔符前面的屬性名稱
  8.            String nestedProperty = propertyPath.substring(0, pos);  
  9.            //獲取第一個屬性分隔符後面的字串
  10.            String nestedPath = propertyPath.substring(pos + 1);  
  11.            //獲取第一個屬性片斷的BeanWrapperImpl物件nestedBw
  12.            BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);  
  13.            //呼叫nestedBw的getBeanWrapperForPropertyPath方法,對第一個屬性分隔符後面的屬性字串進行處理
  14.            return nestedBw.getBeanWrapperForPropertyPath(nestedPath);  
  15.        }  
  16.        //否則,返回this物件本身
  17.        else {  
  18.            returnthis;  
  19.        }  
  20.     }  

在此方法中對巢狀型別的屬性進行了分析,首先將處理第一個.前的屬性,獲取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”]

最頂層的BeanWrapperemployeeBeanWrapper,其包裝的物件是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類代表attrskeylocation的屬性,則其canonicalNameattrs[“location”]

String actualName

儲存當前級別屬性的實際名稱,為[前的字串

如屬性employee.company.attrs[“location”]

EmployeeBeanWrapperImpl類代表company屬性,則actualName值為”company”

如在CompanyBeanWrapperImpl類代表attrskeylocation的屬性,則其actualName”attrs”,取當前屬性級別中”[“前面的字串

String[] keys

代表當前級別屬性中所有位於[與]間的key或索引所組成的陣列

注:“當前級別屬性”表示所要訪問在屬性在屬性全路徑中位於..之間的值

九、 所使用到的工具類

在此過程中所使用到的一些工具類和主要方法如下:

org.springframework.beans.PropertyAccessorUtils  用於分析屬性名稱的工具類,提供分析巢狀屬性、集合屬性(包括.[]等)的一些工具方法。

出自於http://blog.csdn.net/zhiweianran/article/details/7919129