JavaBeans
导航用户界面主题: ) |
任何现代计算机语言的框架都以可重用性为核心。在重复的环境中使用之前构建的组件通常是可取的。在快速应用程序开发中,这些组件被证明更有用,因为您可以将它们从组件列表中拖出并在项目中的其他地方使用它们。借助 JavaBeans 架构,这种级别的可重用性被添加到 Java 编程语言中。
JavaBeans 是主流的 Java 组件模型,由 Sun Microsystems 于 1996 年推出。JavaBeans 的定义如下
"JavaBean 是一个可重用的软件组件,可以在构建工具中以可视方式操作。"
与组件模型一起,Sun 发布了一个简单的可视化组合工具 BeanBox。它主要用于实验 Bean,而不是提供专业的 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 方法或两者来推断的。
- 用于从 bean 获取属性值的 getter 方法。名称通常为
getPropertyName
格式。例如,String 属性whiskey
的 getter 方法是public String getWhiskey()
。对于布尔属性(其类型为 boolean),约定使用isPropertyName
命名模式。
isDiscounted()
将是名为 discounted
的布尔属性的 getter 方法。因此,大多数 getter 方法的签名是 public PropertyType getPropertyName()
或 public boolean isPropertyName()
。
- 用于为 bean 的属性赋值的 setter 方法。setter 方法是
public void setPropertyName(PropertyType value)
格式的方法。对于前面的示例,setter 可以被调用为setWhiskey("bourbon");
。
根据 JavaBeans 标准,按上述方式定义的 getter 和 setter 会自动确定类的属性。但是,通过创建 java.beans.PropertyDescriptor
类,您可以通过显式声明属性名称以及每个属性的 getter 和/或 setter 方法来指定备用实现。
这是一个具有 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()
的函数的程序员想要编写一种简单的方法来从几个可以从 Employee 和 Building bean 获取字段的 bean 中获取“name”字段,并且该方法可以轻松地适应从其他可能尚未编写的 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 编程语言默认支持许多行为(例如平台独立性或打包机制)。但是,可以选择为 bean 装备额外的对象,例如BeanInfos 或自定义 PropertyEditors,以更灵活的方式使用组件模型。第二项功能是 Sun 根据 JavaBeans 组件模型设计了整个 Swing GUI 库。因此,Swing 组件可以很容易地在可视化构建工具中组合起来。
然而,JavaBeans并没有实现组件模型的所有功能。一个缺点是JavaBeans被限制在Java编程语言中,而组件的重要目标是实现语言的独立性。
- Learning JavaTM, Niemeyer, P. 和 Knudsen, J.,第三版,2005,O'Reilly: Sebastopol, CA。第751-786页