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 页