JavaBeans
导航 用户界面 主题: ) |
任何现代计算机语言框架的核心都是可重用性。在重复的环境中使用以前构建的组件通常是可取的。在 快速应用程序开发 中,这些组件被证明更有用,因为您可以从组件列表中拖动它们并在项目中的其他地方使用它们。JavaBeans 架构通过 Java 编程语言添加了这种可重用性级别。
JavaBeans 是主流的 Java 组件模型,由 Sun Microsystems 于 1996 年推出。JavaBeans 定义如下
“JavaBean 是一个可重用的软件组件,可以在构建工具中以可视方式进行操作。”
Sun 除了组件模型外,还发布了一个简单的可视化组合工具 BeanBox。它主要用于试验 Bean,而不是提供专业 IDE。对于实际应用,最好部署在 Visual Age 或 JBuilder 等支持 JavaBeans 可视化组合的 Java IDE 之一上。
正如我们将看到的,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
实例变量实现,但这并非必需。
一个简单的 JavaBean
[edit | edit source]这是一个具有属性 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;
}
}
|
持久性
[edit | edit source]对象成为 Bean 的要求是定义一个公共无参数构造函数,以便构建工具可以以一种简单的方式实例化 Bean(在 Point Bean 中,无参数构造函数是隐式给出的)。其次,需要实现 java.io.Serializable
或 java.io.Externalizable
接口之一。这些接口没有规定任何方法的实现,但表示 Bean 可以保存在持久存储中,例如文件中或数据库中。这样做可以使 Bean 在应用程序关闭或跨网络传输后恢复。在持久存储中保存组件状态的能力称为持久性。Java 提供标准序列化机制,使对象序列化非常容易。或者,可以通过实现 Externalizable
接口以自定义方式存储组件(例如,以 xml 格式)。
属性
[edit | edit source]Bean 的属性是所有通过公共方法可访问和可修改的私有字段。这些 getter 和 setter 方法应通过遵循简单的命名约定标记为这样:对于某个名为 xxx
的属性,应该有一个 getXxx()
返回属性值,还有一个 setXxx()
设置属性。
内省
[edit | edit source]在 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
}
|
事件
[edit | edit source]JavaBeans 通过事件相互交互。事件是通知,一个组件可以告知其他组件,表示某些有趣的事情发生了。事件的示例可能是按钮上的鼠标点击或窗口关闭。Bean 可以是事件的源和目标。要了解事件,Bean 必须在另一个 Bean 中注册为侦听器。
Java 事件模型实现了观察者设计模式,其效果是减少了组件间的耦合。方法调用需要紧密耦合,因为调用者和接收者需要在编译时相互了解,而事件的所有通信都仅通过接口进行。
一种特殊的事件是 PropertyChangeEvents
。它们用于限制某些属性只能取特定值,例如,对于月份,整数值在 1 到 12 之间。每次修改此类绑定属性时,都会向所有已注册的PropertyChangeListeners 发送通知。
自定义
[edit | edit source]自定义通过属性编辑器完成。属性编辑器是一种用于在设计时自定义特定属性类型的工具。属性编辑器从所谓的属性表中激活,属性表显示 Bean 的所有属性。如果选择要自定义的属性,属性表将找出属性的类型,并显示包含属性当前值的适当属性编辑器。
其他说明
[edit | edit source]JavaBean 组件模型的一大优势是它设计为简单。开发 JavaBeans 非常简单,因为许多行为(如平台独立性或打包机制)默认情况下在 Java 编程语言中得到支持。但是,可以可选地为 Bean 装备额外的对象,如BeanInfos 或自定义PropertyEditors,以更灵活的方式使用组件模型。第二种方法是 Sun 根据 JavaBeans 组件模型设计了整个 Swing GUI 库。因此,Swing 组件可以轻松地在可视化构建工具中组合。
但是,JavaBeans 并没有实现组件模型的所有功能。一个缺点是 JavaBeans 仅限于 Java 编程语言,而组件的重要目标是独立于实现语言。
推荐读物
[edit | edit source]- Learning JavaTM,Niemeyer,P. 和 Knudsen,J.,第三版,2005 年,O'Reilly:Sebastopol,CA。第 751-786 页