?
本文檔使用 PHP中文網(wǎng)手冊(cè) 發(fā)布
org.springframework.beans
包遵循Sun發(fā)布的JavaBean標(biāo)準(zhǔn)。JavaBean是一個(gè)簡單的含有一個(gè)默認(rèn)無參數(shù)構(gòu)造函數(shù)的Java類, 這個(gè)類中的屬性遵循一定的命名規(guī)范,且具有setter和getter方法。例如,某個(gè)類擁有一個(gè)叫做bingoMadness
的屬性,并同時(shí)具有與該屬性對(duì)應(yīng)的setter方法:setBingoMadness(..)
和getter方法:getBingoMadness()
。如果你需要了解JavaBean規(guī)范的詳細(xì)信息可以訪問Sun的網(wǎng)站 (java.sun.com/products/javabeans)。
這個(gè)包中的一個(gè)非常重要的類就是BeanWrapper
接口以及它對(duì)應(yīng)的實(shí)現(xiàn)(BeanWrapperImpl
)。根據(jù)JavaDoc中的說明,BeanWrapper
提供了設(shè)置和獲取屬性值(單個(gè)的或者是批量的),獲取屬性描述信息、查詢只讀或者可寫屬性等功能。不僅如此,BeanWrapper
還支持嵌套屬性,你可以不受嵌套深度限制對(duì)子屬性的值進(jìn)行設(shè)置。所以,BeanWrapper
無需任何輔助代碼就可以支持標(biāo)準(zhǔn)JavaBean的PropertyChangeListeners
和VetoableChangeListeners
。除此之外,BeanWrapper
還提供了設(shè)置索引屬性的支持。通常情況下,我們不在應(yīng)用程序中直接使用BeanWrapper
而是使用DataBinder
和BeanFactory
。
BeanWrapper
這個(gè)名字本身就暗示了它的功能:封裝了一個(gè)bean的行為,諸如設(shè)置和獲取屬性值等。
設(shè)置和獲取屬性可以通過使用重載的setPropertyValue(s)
和getPropertyValue(s)
方法來完成。在Spring自帶的JavaDoc中對(duì)它們有詳細(xì)的描述。值得一提的是,在這其中存在一些針對(duì)對(duì)象屬性的潛在約定規(guī)則。下面是一些例子:
表?5.1.?屬性示例
表達(dá)式 | 說明 |
---|---|
name |
表明屬性name與方法getName() 或 isName() 及 setName(..) 相對(duì)應(yīng)。 |
account.name |
指向?qū)傩詀ccount的嵌套屬性name,與之對(duì)應(yīng)的是getAccount().setName()和getAccount().getName() |
account[2] |
指向索引屬性account的第三個(gè)元素,索引屬性可能是一個(gè)數(shù)組(array),列表(list)或其它天然有序的容器。 |
account[COMPANYNAME] |
指向一個(gè)Map實(shí)體account中以COMPANYNAME作為鍵值(key)所對(duì)應(yīng)的值 |
在下面的例子中你將看到一些使用BeanWrapper
設(shè)置屬性的例子。
如果你不打算直接使用BeanWrapper
,這部分不是很重要。如果你僅僅使用DataBinder
和BeanFactory
或者他們的擴(kuò)展實(shí)現(xiàn),你可以跳過這部分直接閱讀PropertyEditor
的部分。
考慮下面兩個(gè)類:
public class Company { private String name; private Employee managingDirector; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Employee getManagingDirector() { return this.managingDirector; } public void setManagingDirector(Employee managingDirector) { this.managingDirector = managingDirector; } }
public class Employee { private String name; private float salary; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } }
下面的代碼片斷展示了如何獲取和設(shè)置上面兩個(gè)示例類 Companies
和Employees
的屬性:
BeanWrapper company = BeanWrapperImpl(new Company()); // setting the company name.. company.setPropertyValue("name", "Some Company Inc."); // ... can also be done like this: PropertyValue value = new PropertyValue("name", "Some Company Inc."); company.setPropertyValue(value); // ok, let's create the director and tie it to the company: BeanWrapper jim = BeanWrapperImpl(new Employee()); jim.setPropertyValue("name", "Jim Stravinsky"); company.setPropertyValue("managingDirector", jim.getWrappedInstance()); // retrieving the salary of the managingDirector through the company Float salary = (Float) company.getPropertyValue("managingDirector.salary");
Spring大量使用了PropertyEditor
以在Object
和 String
之間進(jìn)行有效地轉(zhuǎn)化。仔細(xì)想想,有時(shí)換一種方式來展示屬性的確要比直接用對(duì)象自身更容易讓人理解。例如,Date
可以表示成人們易讀的方式(如String
的方式,'2007-14-09
'),與此同時(shí)我們可以將這種人們比較容易理解的形式轉(zhuǎn)化為原有的原始Date類型(甚至對(duì)于任何人們輸入的可理解的日期形式都可以轉(zhuǎn)化成相應(yīng)的Date
對(duì)象)。要做到這點(diǎn),可以通過注冊(cè)一個(gè)用戶定制編輯器(類型為java.beans.PropertyEditor
)來完成。注冊(cè)一個(gè)用戶自定義的編輯器可以告訴BeanWrapper
我們將要把屬性轉(zhuǎn)換為哪種類型。正如在先前章節(jié)提到的,另外一種選擇是在特定的IoC 容器中完成注冊(cè)。你可以從Sun提供的java.beans
包的JavaDoc中了解到更多PropertyEditors
的細(xì)節(jié)。
屬性編輯器主要應(yīng)用在以下兩個(gè)方面:
使用PropertyEditors
設(shè)置Bean屬性。當(dāng)你在XML文件中聲明的bean的屬性類型為java.lang.String時(shí),Spring將使用ClassEditor
將String解析成Class對(duì)象(如果setter方法需要一個(gè)Class
參數(shù)的話)。
在Spring MVC架構(gòu)中使用各種PropertyEditors
來解析HTTP請(qǐng)求中的參數(shù)。你可以用各種CommandController
的子類來進(jìn)行手工綁定。
Spring提供了許多內(nèi)建的PropertyEditors
可以簡化我們的工作。下面的列表列出了所有Spring自帶的PropertyEditor
,它們都位于org.springframework.beans.
包內(nèi)。它們中的大多數(shù)已經(jīng)默認(rèn)在PropertyEditor
sBeanWrapperImpl
的實(shí)現(xiàn)類中注冊(cè)好了。作為可配置的選項(xiàng),你也可以注冊(cè)你自己的屬性編輯器實(shí)現(xiàn)去覆蓋那些默認(rèn)編輯器。
表?5.2.?內(nèi)建的PropertyEditors
類名 | 說明 |
---|---|
ByteArrayPropertyEditor |
byte數(shù)組編輯器。字符串將被簡單轉(zhuǎn)化成他們相應(yīng)的byte形式。在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了。 |
ClassEditor |
將以字符串形式出現(xiàn)的類名解析成為真實(shí)的Class對(duì)象或者其他相關(guān)形式。當(dāng)這個(gè)Class沒有被找到,會(huì)拋出一個(gè)IllegalArgumentException 的異常,在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了。
|
CustomBooleanEditor |
為Boolean 類型屬性定制的屬性編輯器。在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了,但可以被用戶自定義的編輯器實(shí)例覆蓋其行為。
|
CustomCollectionEditor |
集合(Collection )編輯器,將任何源集合(Collection )轉(zhuǎn)化成目標(biāo)的集合類型的對(duì)象。
|
CustomDateEditor |
為java.util.Date類型定制的屬性編輯器,支持用戶自定義的DateFormat。默認(rèn)沒有被BeanWrapper Impl注冊(cè),需要用戶通過指定恰當(dāng)?shù)膄ormat類型來注冊(cè)。
|
CustomNumberEditor |
為Integer , Long , Float , Double 等Number的子類定制的屬性編輯器。在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了,但可以被用戶自己定義的編輯器實(shí)例覆蓋其行為。
|
FileEditor |
能夠?qū)⒆址D(zhuǎn)化成java.io.File 對(duì)象,在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了。
|
InputStreamEditor |
一個(gè)單向的屬性編輯器,能夠把文本字符串轉(zhuǎn)化成InputStream (通過ResourceEditor 和 Resource 作為中介),因而InputStream 屬性可以直接被設(shè)置成字符串。注意在默認(rèn)情況下,這個(gè)屬性編輯器不會(huì)為你關(guān)閉InputStream 。在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了。
|
LocaleEditor |
在String對(duì)象和Locale 對(duì)象之間互相轉(zhuǎn)化。(String的形式為[語言]_[國家]_[變量],這與Local對(duì)象的toString()方法得到的結(jié)果相同)在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了。
|
PatternEditor |
可以將字符串轉(zhuǎn)化為JDK 1.5的
Pattern 對(duì)象,反之亦然。 |
PropertiesEditor |
能將String轉(zhuǎn)化為Properties 對(duì)象(由JavaDoc規(guī)定的java.lang.Properties類型的格式)。在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了。
|
StringTrimmerEditor |
一個(gè)用于修剪(trim)String類型的屬性編輯器,具有將一個(gè)空字符串轉(zhuǎn)化為null 值的選項(xiàng)。默認(rèn)沒有注冊(cè),必須由用戶在需要的時(shí)候自行注冊(cè)。
|
URLEditor |
能將String表示的URL轉(zhuǎn)化為一個(gè)具體的URL 對(duì)象。在BeanWrapperImpl 中已經(jīng)默認(rèn)注冊(cè)好了。
|
Spring使用java.beans.PropertyEditorManager
來為可能需要的屬性編輯器設(shè)置查詢路徑。查詢路徑同時(shí)包含了sun.bean.editors
,這個(gè)包中定義了很多PropertyEditor
的具體實(shí)現(xiàn),包括Font
、Color
以及絕大多數(shù)的基本類型的具體實(shí)現(xiàn)。同樣值得注意的是,標(biāo)準(zhǔn)的JavaBean基礎(chǔ)構(gòu)架能夠自動(dòng)識(shí)別PropertyEditor
類(無需做額外的注冊(cè)工作),前提條件是,類和處理這個(gè)類的Editor位于同一級(jí)包結(jié)構(gòu),而Editor的命名遵循了在類名后加了“Editor”的規(guī)則。舉例來說,當(dāng)FooEditor
和Foo
在同一級(jí)別包下的時(shí)候,FooEditor
能夠識(shí)別Foo
類并作為它的PropertyEditor
。
com
chank
pop
Foo
FooEditor // the PropertyEditor
for the Foo
class
注意,你同樣可以使用標(biāo)準(zhǔn)的BeanInfo
JavaBean機(jī)制(詳情見這里)。在下面的例子中,你可以看到一個(gè)通過使用BeanInfo
機(jī)制來為相關(guān)類的屬性明確定義一個(gè)或者多個(gè)PropertyEditor
實(shí)例。
com
chank
pop
Foo
FooBeanInfo // the BeanInfo
for the Foo
class
下面就是FooBeanInfo
類的源碼,它將CustomNumberEditor
與Foo
中的age
屬性聯(lián)系在了一起。
public class FooBeanInfo extends SimpleBeanInfo { public PropertyDescriptor[] getPropertyDescriptors() { try { final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true); PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Foo.class) { public PropertyEditor createPropertyEditor(Object bean) { return numberPE; }; }; return new PropertyDescriptor[] { ageDescriptor }; } catch (IntrospectionException ex) { throw new Error(ex.toString()); } } }
當(dāng)以一個(gè)字符串值來設(shè)置bean屬性時(shí),Spring IoC 容器最終使用標(biāo)準(zhǔn)的JavaBean PropertyEditor
來將這些字符串轉(zhuǎn)化成復(fù)雜的數(shù)據(jù)類型。Spring預(yù)先注冊(cè)了一些PropertyEditor
(舉例來說,將一個(gè)以字符串表示的Class轉(zhuǎn)化成Class
對(duì)象)。除此之外,Java標(biāo)準(zhǔn)的JavaBean PropertyEditor
會(huì)識(shí)別在同一包結(jié)構(gòu)下的類和它對(duì)應(yīng)的命名恰當(dāng)?shù)腅ditor,并自動(dòng)將其作為這個(gè)類的的Editor。
如果你想注冊(cè)自己定義的PropertyEditor
,那么有幾種不同的機(jī)制供君選擇。其中,最原始的手工方式是在你有一個(gè)BeanFactory
的引用實(shí)例時(shí),使用ConfigurableBeanFactory
的registerCustomEditor()
方法。當(dāng)然,通常這種方法不夠方便,因而并不推薦使用。另外一個(gè)簡便的方法是使用一個(gè)稱之為CustomEditorConfigurer
的特殊的bean factory后置處理器。盡管bean factory的后置處理器可以半手工化的與BeanFactory
實(shí)現(xiàn)一起使用,但是它存在著一個(gè)嵌套屬性的建立方式。因此,強(qiáng)烈推薦的一種做法是與ApplicationContext
一起來使用它。這樣就能使之與其他的bean一樣以類似的方式部署同時(shí)被容器所感知并使用。
注意所有的bean factory和application context都會(huì)自動(dòng)地使用一系列的內(nèi)置屬性編輯器,通過BeanWrapper
來處理屬性的轉(zhuǎn)化。在這里列出一些在BeanWrapper
中注冊(cè)的標(biāo)準(zhǔn)的屬性編輯器。除此之外,ApplicationContext
覆蓋了一些默認(rèn)行為,并為之增加了許多編輯器來處理在某種意義上合適于特定的application context類型的資源查找。
標(biāo)準(zhǔn)的JavaBean的PropertyEditor
實(shí)例將以String表示的值轉(zhuǎn)化成實(shí)際復(fù)雜的數(shù)據(jù)類型。CustomEditorConfigurer
作為一個(gè)bean factory的后置處理器,能夠便捷地將一些額外的PropertyEditor
實(shí)例加入到ApplicationContext
中去。
考慮用戶定義的類ExoticType
和DependsOnExoticType
,其中,后者需要將前者設(shè)置為它的屬性:
package example; public class ExoticType { private String name; public ExoticType(String name) { this.name = name; } } public class DependsOnExoticType { private ExoticType type; public void setType(ExoticType type) { this.type = type; } }
在一切建立起來以后,我們希望通過指定一個(gè)字符串來設(shè)置type屬性的值,然后PropertyEditor
將在幕后幫你將其轉(zhuǎn)化為實(shí)際的ExoticType
對(duì)象:
<bean id="sample" class="example.DependsOnExoticType"> <property name="type" value="aNameForExoticType"/> </bean>
PropertyEditor
的實(shí)現(xiàn)看上去就像這樣:
// converts string representation to ExoticType
object
package example;
public class ExoticTypeEditor extends PropertyEditorSupport {
private String format;
public void setFormat(String format) {
this.format = format;
}
public void setAsText(String text) {
if (format != null && format.equals("upperCase")) {
text = text.toUpperCase();
}
ExoticType type = new ExoticType(text);
setValue(type);
}
}
最后,我們通過使用CustomEditorConfigurer
來為ApplicationContext
注冊(cè)一個(gè)新的PropertyEditor
,這樣,我們就可以在任何需要的地方使用它了:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="example.ExoticType"> <bean class="example.ExoticTypeEditor"> <property name="format" value="upperCase"/> </bean> </entry> </map> </property> </bean>
將屬性編輯器注冊(cè)到Spring容器的另一種方式是創(chuàng)建并使用PropertyEditorRegistrar
。
當(dāng)你在不同情況下(如編寫一個(gè)相應(yīng)的注冊(cè)器然后在多種情況下重用它)需要使用相同的屬性編輯器時(shí)該接口特別有用。
PropertyEditorRegistrars
與PropertyEditorRegistry
接口協(xié)同工作,
PropertyEditorRegistry
是一個(gè)由Spring的BeanWrapper
(及DataBinder
)實(shí)現(xiàn)的一個(gè)接口。
當(dāng)與CustomEditorConfigurer
(在此處曾介紹過)協(xié)同使用時(shí),
PropertyEditorRegistrars
尤為方便,
CustomEditorConfigurer
會(huì)暴露一個(gè)叫做setPropertyEditorRegistrars(..)
的方法:
在這種情況下DataBinder
和Spring MVC Controllers
會(huì)很容易地共享加到CustomEditorConfigurer
中的PropertyEditorRegistrars
。
此外,自定義編輯器無需再進(jìn)行同步了:每次創(chuàng)建bean時(shí)PropertyEditorRegistrar
都會(huì)創(chuàng)建一個(gè)新的PropertyEditor
。
通過示例最能說明PropertyEditorRegistrar
的優(yōu)點(diǎn)了。首先,你需要?jiǎng)?chuàng)建你自己的PropertyEditorRegistrar
實(shí)現(xiàn):
package com.foo.editors.spring;
public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
// it is expected that new PropertyEditor
instances are created
registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
// you could register as many custom property editors as are required here...
}
}
org.springframework.beans.support.ResourceEditorRegistrar
是另一個(gè)PropertyEditorRegistrar
實(shí)現(xiàn)的范例。請(qǐng)注意在registerCustomEditors(..)
方法的實(shí)現(xiàn)中,它是如何創(chuàng)建每個(gè)屬性編輯器的實(shí)例的。
接下來我們配置一個(gè)CustomEditorConfigurer
,然后將CustomPropertyEditorRegistrar
實(shí)例注入其中:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="propertyEditorRegistrars"> <list> <ref bean="customPropertyEditorRegistrar"/> </list> </property> </bean> <bean id="customPropertyEditorRegistrar" class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>
最后,稍微偏離一下本章的主題,對(duì)于使用了Spring的MVC web框架的應(yīng)用來說,與數(shù)據(jù)綁定Controllers
(例如SimpleFormController
)一同使用PropertyEditorRegistrars
會(huì)非常便捷。在下面的例子中,initBinder(..)
方法的實(shí)現(xiàn)使用了PropertyEditorRegistrar
:
public final class RegisterUserController extends SimpleFormController {
private final PropertyEditorRegistrar customPropertyEditorRegistrar;
public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
this.customPropertyEditorRegistrar = propertyEditorRegistrar;
}
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
this.customPropertyEditorRegistrar.registerCustomEditors(binder);
}
// other methods to do with registering a User
}
這種注冊(cè)PropertyEditor
的形式使得代碼非常簡練(initBinder(..)
的實(shí)現(xiàn)就一行代碼而已!),同時(shí)它使得通用的PropertyEditor
注冊(cè)代碼可被封裝到一個(gè)類中,然后在需要時(shí)被多個(gè)Controllers
共享。