說說 Spring 的屬性編輯器
在 Spring 配置檔案中,我們往往通過字面值設定 Bean 各種型別的屬性值 ,這個功能是通過屬性編輯器實現的。
任何實現了 java.beans.PropertyEditor 介面的類都是屬性編輯器 。 它可以將外部需要設定的值轉換為 JVM 內部的對應型別,所以屬性編輯器其實就是一個型別轉換器 。
1 JavaBean 編輯器
Sun 所制定的 JavaBean 編輯器,很大程度上是為 IDE 準備的。它讓 IDE 能夠以視覺化的方式來設定 JavaBean 的屬性 。
Java 通過 java.beans.PropertyEditor 定義了設定 JavaBean 屬性的方法,通過 BeanInfo 描述了 JavaBean 哪些屬性是可定製的,此外還描述了可定製屬性與 PropertyEditor 之間的對應關係 。
BeanInfo 與 JavaBean 之間的對應關係,通過兩者之間規範的命名確立,對應 JavaBean 的 BeanInfo 採用如下命名規範:<Bean>BeanInfo
。 如 BookBean 對應的 BeanInfo 為 BookBeanBeanInfo。 當 JavaBean 連同其屬性編輯器註冊到 IDE 後,當在開發介面中對 JavaBean 進行定製時, IDE 就會根據 JavaBean 規範找到對應的 BeanInfo ,然後再根據 BeanInfo 中的描述資訊找到 JavaBean 屬性描述(使用哪個屬性編輯器等),進而為 JavaBean 生成特定開發編輯介面 。
Java 提供了一個用於管理預設屬性編輯器的管理器: PropertyEditorManager ,它儲存著一些常見型別的屬性編輯器,如果某個 JavaBean 的常見型別屬性沒有通過 BeanInfo 顯式指定它的屬性編輯器, 那麼 IDE 將自動使用 PropertyEditorManager 中註冊的對應屬性的預設編輯器 。
1.1 屬性編輯器(PropertyEditor)
PropertyEditor 是屬性編輯器介面,它定義了將外部設定值轉換為內部 JavaBean 屬性值的介面方法 。主要介面方法說明如下:
方法 | 說明 |
---|---|
Object getValue() | 返回屬性的當前值 ,基本型別被封裝成對應的包裝型別 |
void setValue(Object newValue) | 設定屬性的值,基本型別以包裝型別傳入 |
String getAsText() | 用字串來表示屬性物件,以便外部的屬性編輯器能夠以視覺化的方式顯示 。 預設返回 null ,表示該屬性不能以字串表示。 |
void setAsText(String text) | 用一個字串去更新屬性的內部值,它一般從外部屬性編輯器傳入的。 |
String[] getTags() | 返回表示有效屬性值的字串陣列,以便屬性編輯器能夠以下拉框的方式進行展示 。 預設返回 null。 |
String getJavaInitializationString() | 為屬性提供初始值,屬性編輯器以此值作為屬性的預設值 。 |
PropertyEditor 介面是內部屬性值和外部設定值的溝通橋樑 。
Java 為 PropertyEditor 提供了一個方便的實現類: PropertyEditorSupport ,該類實現了 PropertyEditor 介面,我們可以通過擴充套件這個類來設計自己的屬性編輯器 。
1.2 Bean 屬性描述(BeanInfo)
BeanInfo 描述了 JavaBean 中的可編輯屬性以及對應的屬性編輯器,每一個屬性對應一個屬性描述器 PropertyDescriptor。
PropertyDescriptor 的建構函式有兩個入參: PropertyDescriptor(String propertyName, Class beanClass)
,其中 propertyName 為屬性名; beanClass 是 JavaBean 所對應的 Class。
PropertyDescriptor 還有一個 setPropertyEditorClass(Class propertyEditorClass) 方法,它可以為 JavaBean 屬性指定編輯器 。
BeanInfo 介面中最重要的方法是:PropertyDescriptor[] getPropertyDescriptors()
,它會返回 JavaBean 的屬性描述器陣列 。
BeanInfo 介面的一個常用的實現類是 SimpleBeanInfo ,我們可以通過擴充套件這個類來實現自定義的功能 。
2 Spring 預設屬性編輯器
Spring 的屬性編輯器與傳統的用於 IDE 開發的屬性編輯器不同,它沒有 UI 介面,只是將配置檔案中的文字配置值轉換為 Bean 屬性的對應值 。
Spring 在 PropertyEditorRegistrySupport 中為常見的屬性型別提供了預設屬性編輯器,分為 3 大類,共有 32 個:
型別 | 說明 |
---|---|
基礎資料型別 | 【1】基本資料型別,如: boolean、int 等; 【2】基本資料型別封裝類,如: Boolean、Integer 等; 【3】基本資料型別陣列: char[] 和 byte[] ; 【4】大數: BigDecimal 和 BigInteger 。 |
集合類 | Collection、Set、SortedSet、List 和 SortedMap。 |
資源類 | Class、Class[]、File、InputStream、Locale、Properties、Resource[] 和 URL。 |
PropertyEditorRegistrySupport 中有兩個用於儲存屬性編輯器的 Map 型別變數:
變數名 | 說明 |
---|---|
defaultEditors | 儲存預設屬性型別的編輯器,元素的鍵為屬性型別,值為對應的屬性編輯器例項。 |
customEditors | 儲存使用者自定義的屬性編輯器,元素的鍵值和 defaultEditors 相同 。 |
3 自定義 Spring 屬性編輯器
如果我們的應用定義了特殊型別的屬性,並且希望在配置檔案中以字面值方式來配置屬性值,那麼就可以編寫自定義屬性編輯器並註冊到 Spring 容器的方式來實現。
Spring 預設的屬性編輯器大都擴充套件自 java.beans.PropertyEditorSupport
,我們可以通過擴充套件 PropertyEditorSupport 來自定義屬性編輯器 。在Spring 環境下僅需要將配置檔案中字面值轉換為屬性型別的物件即可,並不需要提供 UI 介面,所以僅需要覆蓋 PropertyEditorSupport 的 setAsText() 方法就可以啦 (∩_∩)O哈哈~。
假設我們有兩個實體 Book 和 Author,希望在配置 Book 時,可以直接設定 Author 的名字。
Book.java
public class Book {
/**
* 作者
*/
private Author author;
/**
* 書名
*/
private String name;
//省略 get/setter 方法
}
Author.java
public class Author {
private String name;
//省略 get/setter 方法
}
首先,自定義 author 的屬性編輯器:
public class CustomPropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if(text==null||text.length()==0){
throw new IllegalArgumentException("格式錯誤");
}
Author author=new Author();
author.setName(text);
//呼叫父類的方法,來設定屬性物件
setValue(author);
}
}
如果使用 BeanFactory ,則需要手工呼叫 registerCustomEditor(Class requiredType, PropertyEditor propertyEditor)
方法註冊自定義屬性編輯器;如果使用的是 ApplicationContext ,那麼只需要在配置檔案中註冊 CustomEditorConfigurer 即可 。CustomEditorConfigurer 實現了BeanFactoryPostProcessor 介面,所以它是一個 Bean 的工廠後處理器 。
現在註冊自定義的屬性編輯器:
<!-- 註冊自定義的屬性編輯器-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<!-- key:屬性型別;value:屬性編輯器-->
<entry key="net.deniro.spring4.editor.Author"
value="net.deniro.spring4.editor.CustomPropertyEditor"
/>
</map>
</property>
</bean>
<bean id="book" class="net.deniro.spring4.editor.Book">
<property name="name" value="海邊的卡夫卡"/>
<!-- 使用之前定義的編輯器注入該屬性-->
<property name="author" value="村上春樹"/>
</bean>
BeanWrapper 在設定 book 的 author 屬性時,將檢索自定義屬性編輯器登錄檔,當發現 author 屬性型別所對應的屬性編輯器 CustomPropertyEditor 時,它就會這個定製的屬性編輯器把 “村上春樹” 轉換為 Author 物件 。
按照規範, Java 會在 JavaBean 的相同類包下查詢是否存在 <JavaBean>Editor
的類;如果存在,就會自動使用 <JavaBean>Editor
作為該 JavaBean 的屬性編輯器 。Spring 也支援這個規範。
所以如果在類包下有一個名為 AuthorEditor 屬性編輯器類,那麼就無須在配置檔案中註冊自定義的屬性編輯器啦O(∩_∩)O哈哈~