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 方法或两者来推断的。
- 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 方法来指定备用实现。
属性通常使用私有的实例变量来实现,但这不是必需的。
一个简单的 JavaBean
[edit | edit source]这是一个简单的 JavaBean 类型示例,它具有属性 int age
和 String color
。
代码清单 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,另一个包含公司占用的建筑物列表。编写名为 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 编程语言支持许多行为(如平台独立性或打包机制)。但是,可以选择用额外的对象(如BeanInfos 或自定义的 PropertyEditors)来装备 bean,以更灵活的方式使用组件模型。第二个便利是 Sun 根据 JavaBeans 组件模型设计了整个 Swing GUI 库。因此,Swing 组件可以轻松地在可视化构建工具中进行组合。
但是,JavaBeans 并没有实现组件模型的所有功能。一个缺点是 JavaBeans 被限制在 Java 编程语言中,而组件的一个重要目标是实现语言的独立性。
推荐读物
[edit | edit source]- 学习 JavaTM,Niemeyer,P. 和 Knudsen,J.,第 3 版,2005 年,O'Reilly:Sebastopol,CA。第 751-786 页