重復是理解和記憶的最好方法。在講實例化Bean的每個步驟之前,我都會先復習一下Bean實例化的整個過程:
結合圖片我們回顧一下具體的過程:
- ResourceLoader加載配置信息,
- 由BeanDefinitionReader讀取並解析<bean>標簽,並將<bean>標簽的屬性都轉換為BeanDefinition對應的屬性,並注冊到BeanDefinitionRegistry注冊表中。
- 容器掃描注冊表,通過反射機制獲取BeanFactoryPostProcessor類型的工廠后處理器,並用這個工廠后處理器對BeanDefinition進行加工。
- 取出加工過的BeanDefinition,使用InstantiationStrategy實例化Bean。
- BeanWrapper結合BeanDefinitionRegistry和PropertyEditorRegistry對Bean的屬性賦值。
今天我們將介紹的就是第五步。開門見山,BeanWrapper的功能:
spring通過BeanWrapper完成屬性的配置工作。具體表現為:
- 從BeanDefinitionRegistry注冊表中取出尚未進行屬性配置的BeanDefinition,獲取Bean屬性的配置信息,
- 使用屬性編輯器對這些配置信息進行轉換得到Bean屬性的值,
- 最后對Bean通過反射機制設置屬性值。
下面是BeanWrapper的繼承結構:
從上面的結構可以看出,BeanWrapperImpl(BeanWrapper的實現類)有兩個頂級接口,分別是:PropertyEditorRegistry和PropertyAccessor,前者是屬性編輯器,負責將配置文件中bean屬性的字面值轉換為bean具體的屬性值。后者定義了各種訪問bean屬性的方法。所以BeanWrapperImpl具有三重身份:
1. Bean包裹器:(顧名思義)
下面代碼是BeanWrapperImpl初始化時要執行的方法,而參數中的object就是包裹的bean對象。
其中第8行就是把object.getClass()保存在cachedIntrospectionResults 屬性中,該屬性是CachedIntrospectionResults類的實例,
而CachedIntrospectionResults是負責緩存屬性描述器(PropertyDescriptor)信息的。
object是對象的實例,而object.getClass()則獲取的是實例對應的類的描述信息,那么cachedIntrospectionResults 拿到這個實例類的描述信息class,就可以通過反射機制來訪問該class里面的所有屬性了,最后封裝成PropertyDescriptor。
屬性描述器(PropertyDescriptor)是java.beans.PropertyDescriptor包里的類,用來描述java bean的屬性(一個描述器描述一個屬性),這個屬性是JavaBean通過一對入口方法導出的。
BeanWrapperImpl利用屬性描述器信息結合屬性編輯器來設置屬性。
1 public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { 2 Assert.notNull(object, "Bean object must not be null"); 3 this.object = object; 4 this.nestedPath = (nestedPath != null ? nestedPath : ""); 5 this.rootObject = (!"".equals(this.nestedPath) ? rootObject : object); 6 this.nestedBeanWrappers = null; 7 this.typeConverterDelegate = new TypeConverterDelegate(this, object); 8 setIntrospectionClass(object.getClass()); 9 }
1 protected void setIntrospectionClass(Class clazz) { 2 if (this.cachedIntrospectionResults != null && 3 !clazz.equals(this.cachedIntrospectionResults.getBeanClass())) { 4 this.cachedIntrospectionResults = null; 5 } 6 }
2. 屬性訪問器:即PropertyAccessor,這個接口有很多方法,諸如:setPropertyValue、setPropertyValues等,BeanWrapperImpl通過這些方法來設置bean屬性的值。
3. 屬性編輯器注冊表:(負責取出屬性編輯器,BeanWrapperImpl結合屬性編輯器來設置屬性)。
下面是BeanWrapperImpl的某個構造函數,一開始就調用了從屬性編輯器那里繼承過來的registerDefaultEditors方法,該方法自動注冊加載spring默認的屬性編輯器們。
1 public BeanWrapperImpl(Object object) { 2 registerDefaultEditors(); 3 setWrappedInstance(object); 4 }
BeanWrapperImpl完成Bean屬性的配置工作之后,接下來還需要Bean后處理器(實現了BeanPostProcessor接口的Bean)繼續對Bean實例進行加工,直到裝配出一個准備就緒的Bean,把肉放到碗里等萌寶來吃。
由於本文未對代碼進行詳細解讀,所以其中會有沒有講到的地方,比如,屬性編輯器具體是怎樣的?如何對bean的屬性進行編輯?下一篇博文將會詳細介紹。