JavaBeans
| 導航 使用者介面 主題: |
可重用性是任何現代計算機語言框架的核心。在重複的環境中使用之前構建的元件通常是可取的。在 快速應用程式開發 中,這些元件更有用,因為您可以從元件列表中拖動它們並在專案的其他地方使用。JavaBeans 架構透過 Java 程式語言添加了這種可重用性。
JavaBeans 是主流的 Java 元件模型,由 Sun Microsystems 於 1996 年推出。JavaBeans 定義如下
"JavaBean 是一個可重用的軟體元件,可以在構建工具中進行視覺化操作。"
Sun 與元件模型一起釋出了一個簡單的視覺化組合工具 BeanBox。它主要用於實驗 Beans,而不是提供專業 IDE。對於實際應用,最好部署在支援 JavaBeans 視覺化組合的 Java IDE(如 Visual Age 或 JBuilder)中。
正如我們將看到的,JavaBeans 本質上與標準 Java 類沒有區別,這使得元件模型非常易於使用。使 JavaBean 與普通 Java 類區別開來的是,JavaBean 遵循 Oracle JavaBeans 標準(術語 JavaBean 或簡稱為 bean 也指 JavaBean 類的例項)。為了便於重用,已經採用了一組功能和約定
- 存在一個無引數建構函式;
- 支援永續性;
- 由 getter 和 setter 方法操作的屬性;
- 支援內省;
- 事件作為 bean 之間通訊的機制;
- 支援透過屬性編輯器進行自定義。
JavaBeans 標準提供了一個框架,用於建立供 GUI 工具(包括 Java 開發環境)使用的物件。但在更常見的用法中,bean 是一個可序列化的類,它遵循 JavaBeans 的屬性命名約定。這些命名標準使使用 Java 內省 變得容易。
為了遵循這些標準,Bean 需要一個或多個 *屬性* 抽象,它們表示物件的不同的狀態值。屬性有一個名稱(一個有效的 Java 識別符號)和一個型別(可以是 引用型別、基本型別 或者 陣列型別)。預設情況下,JavaBean 類的屬性由存在 getter 方法、setter 方法或兩者推斷。
- getter 方法用於從 Bean 獲取屬性的值。名稱通常採用
getPropertyName的形式。例如,String 屬性whiskey的 getter 方法是public String getWhiskey()。對於布林屬性(其型別為 boolean),慣例是使用命名模式isPropertyName。
isDiscounted() 將是名為 discounted 的布林屬性的 getter 方法。因此,大多數 getter 的方法簽名為 public PropertyType getPropertyName() 或 public boolean isPropertyName()。
- setter 方法用於將值分配給 Bean 的屬性。setter 方法是採用
public void setPropertyName(PropertyType value)形式的方法。對於前面的示例,setter 可以被呼叫為setWhiskey("bourbon");。
根據 JavaBeans 標準,按照上述定義的 getter 和 setter 會自動確定類的屬性。但是,透過建立 java.beans.PropertyDescriptor 類,您可以透過顯式宣告屬性名稱以及每個屬性的 getter 和/或 setter 方法來指定備用實現。
屬性通常使用 private 例項變數 實現,但這不是必需的。
這是一個帶有 int age 和 String color 屬性的簡單 JavaBean 型別的示例。
程式碼清單 9.12:Puppy.java
class Puppy implements java.io.Serializable {
private static final long serialVersionUID = 348652158488L;
private String color;
private int age;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
|
物件成為 Bean 的要求是定義一個公共的無引數建構函式,以便構建器工具能夠以簡單的方式例項化 Bean(在 Point Bean 中,無引數建構函式是隱式給出的)。其次,需要實現 java.io.Serializable 或 java.io.Externalizable 介面之一。這些介面沒有規定任何方法的實現,但它們表示 Bean 可以儲存在永續性儲存中,例如檔案或資料庫中。這樣,在應用程式關閉或透過網路傳輸後,可以恢復 Bean。將元件狀態儲存在永續性儲存中的能力稱為 *永續性*。Java 提供了一個標準的序列化機制,它使序列化物件變得非常容易。或者,可以透過實現 Externalizable 介面以自定義方式儲存元件(例如以 xml 格式)。
Bean 的 *屬性* 是所有透過公共方法訪問和修改的私有欄位。這些 getter 和 setter 方法應透過遵循簡單的命名約定來標記:對於名為 xxx 的屬性,應該有一個 getXxx() 返回屬性值,以及一個 setXxx() 設定屬性。
在 Java 和 J2EE 程式設計中,只有將物件強制轉換為宣告它的類或介面時,才能在物件上呼叫方法。Bean 屬性的結構化定義對於比較幾個不相關也不應該透過繼承相關聯的物件的單個公共屬性非常有用。
例如,程式可能包含一個表示公司員工的 Bean,以及另一個包含公司佔用的建築物列表的 Bean。編寫名為 listAssetNames() 的函式的程式設計師希望編寫一種簡單的方法來從幾個 Bean 中獲取 "name" 欄位,這些 Bean 可以從 Employee 和 Building Bean 中獲取欄位,並且可以輕鬆地適應從可能尚未編寫的其他型別的 Bean 中獲取相同欄位。
雖然這可以透過重新編寫 Employee 和 Building 使它們都從名為 NamedObject 的一個類繼承,或者透過建立一個 NamedObject 介面來完成,但這兩種方法都有自己的問題。使用繼承是有限制的,因為每個子類只能從一個父類繼承,這限制了可以共享屬性的類的數量,以及可以共享的公共屬性的數量。此外,使用繼承來表達除了簡單的 "is-a" 關係以外的關係會令人困惑,就像檢視數十個類定義以找到一個簡單的 "啞" getter 函式的實現位置一樣。建立介面解決了由於多重繼承規則導致的問題,因為一個類可以使用任何數量的介面,但它仍然需要明確地寫出每個共享屬性。
處理這種關係的最簡單、最優雅的方法是使用內省讀取 Bean 屬性。Jakarta BeanUtils 包是處理需要以這種方式關聯的物件的常用方法,因為它利用了 JavaBean 命名約定的規律性。
程式碼清單 9.13:從 JavaBean 獲取屬性
public static Object getProperty(Object o, String propertyName) {
if (o == null ||
propertyName == null ||
propertyName.length() < 1) {
return null;
}
// Based on the property name build the getter method name
String methodName = "get" +
propertyName.substring(0,1).toUpperCase() +
propertyName.substring(1);
Object property = null;
try {
java.lang.Class c = o.getClass();
java.lang.reflect.Method m = c.getMethod(methodName, null);
property = m.invoke(o, null);
} catch (NoSuchMethodException e) {
// Handle exception
} catch (SecurityException e) {
// No permission; Handle exception
}
return property;
}
|
或
程式碼清單 9.14:使用 Apache Commons BeanUtils
import org.apache.commons.beanutils.PropertyUtils;
try {
Object myValue = PropertyUtils.getSimpleProperty(o, propertyName);
} catch (IllegalAccessException e) {
// Handle exception
} catch (InvocationTargetException e) {
// Handle exception
} catch (NoSuchMethodException e) {
// Handle exception
}
|
JavaBeans 透過 *事件* 互相互動。事件是通知,一個元件可以告知其他元件,一些有趣的事情發生了。例如,點選按鈕或關閉視窗都是事件。Bean 可以是事件的源和目標。要接收事件通知,Bean 必須在另一個 Bean 上註冊為 *監聽器*。
Java 事件模型實現了觀察者設計模式,其效果是減少了元件之間的耦合。方法呼叫需要緊耦合,因為呼叫者和接收者需要在編譯時互相瞭解,而使用事件,所有通訊都僅透過介面進行。
有一種特殊的事件,即 PropertyChangeEvents。它們用於限制某些屬性只能取特定值,例如,對於月份,整數值應在 1 到 12 之間。每當這樣的 *繫結屬性* 被修改時,都會向所有註冊的 *PropertyChangeListeners* 傳送通知。
自定義透過 *屬性編輯器* 完成。屬性編輯器是設計時用於自定義特定屬性型別的工具。屬性編輯器從所謂的 *屬性表* 中啟用,屬性表顯示 Bean 的所有屬性。如果選擇要自定義的屬性,屬性表會找出屬性的型別,並顯示帶有屬性當前值的相應屬性編輯器。
JavaBean 元件模型的一大優勢是它旨在保持簡單。開發 JavaBeans 非常簡單,因為許多行為(例如平臺獨立性或打包機制)預設情況下在 Java 程式語言中得到支援。但是,可以選擇用額外的物件(例如 *BeanInfos* 或自定義 *PropertyEditors*)來裝備 Bean,以便以更靈活的方式使用元件模型。第二個設施是 Sun 根據 JavaBeans 元件模型設計了整個 Swing GUI 庫。因此,Swing 元件可以輕鬆地在視覺化構建器工具中組合。
但是,JavaBeans 沒有實現元件模型的所有功能。一個缺點是 JavaBeans 僅限於 Java 程式語言,而元件的重要目標是獨立於實現語言。
- 學習 JavaTM,Niemeyer,P. 和 Knudsen,J.,第三版,2005,O'Reilly:Sebastopol,CA。第 751-786 頁