JavaBean 的事件
一、事件概述
事件处理是JavaBean体系结构的核心之一。通过事件处理机制,可让一些组件作为事件源,发出可被描述环境或其它组件接收的事件。这样,不同的组件就可在构造工具内组合在一起,组件之间通过事件的传递进行通信,构成一个应用。从概念上讲,事件是一种在"源对象"和"监听者对象"之间,某种状态发生变化的传递机制 。事件有许多不同的用途,例如在Windows系统中常要处理的鼠标事件、窗口边界改变事件、键盘事件等。在Java和JavaBean中则是定义了一个一般的、可扩充的事件机制。
主要构成有:
事件从事件源到监听者的传递是通过对目标监听者对象的 Java 方法调用进行的。 对每个明确的事件的发生,都相应地定义一个明确的 Java 方法。这些方法都集中定义在事件监听者( EventListener )接口中,这个接口要继承java.util.EventListener 。 实现了事件监听者接口中一些或全部方法的类就是事件监听者。 伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自 java.util.EventObject 。事件状态对象作为单参传递给对应响应该事件的监听者方法中。 发出某种特定事件的事件源的标识是:遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。 有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例,来建立它们之间的联系。
二、事件状态对象( Event State Object )
与事件发生有关的状态信息一般都封装在一个事件状态对象中 ,这种对象是 java.util.EventObject 的子类。
public class EventObject implements java.io.Serializable {
//事件源
protected transient Object source;
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
public Object getSource() {
return source;
}
public String toString() {
return getClass().getName() + "[source=" + source + "]";
}
}
按设计习惯,这种事件状态对象类的名应以 Event 结尾。
例如:
package javabeanevent;
import java.awt.Point;
public class MouseMovedExampleEvent extends java.util.EventObject {
protected int x, y;
/* 创建一个鼠标移动事件MouseMovedExampleEvent */
MouseMovedExampleEvent(java.awt.Component source, Point location) {
super(source);
x = location.x;
y = location.y;
}
/* 获取鼠标位置*/
public Point getLocation() {
return new Point(x, y);
}
}
三、事件侦听者接口( EventListener Interface )与事件侦听者
由于Java 事件模型是基于方法调用,因而需要一个定义并组织事件操纵方法的方式。JavaBean 中,事件操纵方法都被定义在继承了java.util.EventListener 类的EventListener 接口中,它是一个标志性接口,源码如下:
public interface EventListener {
}
按规定, EventListener 接口的命名要以 Listener 结尾。任何一个类如果想操纵在 EventListener 接口中定义的方法都必须以实现这个接口方式进行。这个类也就是事件监听者。
package javabeanevent;
/* 定义了鼠标移动事件的监听者接口 */
interface MouseMovedExampleListener extends java.util.EventListener {
/* 在这个接口中定义了鼠标移动事件监听者所应支持的方法*/
void mouseMoved(MouseMovedExampleEvent mme);
}
在接口中只定义方法名,方法的参数和返回值类型。如:上面接口中的 mouseMoved 方法的具体实现是在下面的 ArbitraryObject 类中定义的。
package javabeanevent;
public class ArbitraryObject implements MouseMovedExampleListener {
public void mouseMoved(MouseMovedExampleEvent mme) {
//...
}
}
ArbitraryObject 就是MouseMovedExampleEvent 事件的监听者。
四、事件侦听者的注册与注销
为了各种可能的事件监听者把自己注册入合适的事件源中,建立源与事件监听者间的事件流,事件源必须为事件监听者提供注册和注销的方法。
public void add< ListenerType>(< ListenerType> listener) ;
public void remove< ListenerType>(< ListenerType> listener) ;
例如:
首先定义了一个事件监听者接口:
package javabeanevent;
import java.util.EventObject;
public interface ModelChangedListener extends java.util.EventListener {
void modelChanged(EventObject e);
}
接着定义事件源类:
package javabeanevent;
import java.util.EventObject;
import java.util.Vector;
public abstract class Model {
private Vector listeners = new Vector(); // 定义了一个储存事件监听者的数组
/*上面设计格式中的< ListenerType>在此处即是下面的ModelChangedListener*/
public synchronized void addModelChangedListener(ModelChangedListener mcl) {
listeners.addElement(mcl);
}//把监听者注册入listeners数组中
public synchronized void removeModelChangedListener(ModelChangedListener mcl) {
listeners.removeElement(mcl); //把监听者从listeners中注销
}
/*以上两个方法的前面均以synchronized,是因为运行在多线程环境时,可能同时有几个对象同时要进行注册和注销操作,使用synchronized来确保它们之间的同步。开发工具或程序员使用这两个方法建立源与监听者之间的事件流*/
protected void notifyModelChanged() {
/*事件源使用本方法通知监听者发生了modelChanged事件*/
Vector l;
EventObject e = new EventObject(this);
/* 首先要把监听者拷贝到l数组中,冻结EventListeners的状态以传递事件。
* 这样来确保的在循环发送事件时,即使其它线程对监听者集合进行了增删,也不会
* 影响原来需要触发的监听者集合。这里的同步块只是在拷贝时同步,在调用时没同步,
* 因为调用监听者方法或能是个长方法,所以先拷贝*/
synchronized (this) {
l = (Vector) listeners.clone();
}
for (int i = 0; i < l.size(); i++) {
/* 依次通知注册在监听者队列中的每个监听者发生了modelChanged事件,
并把事件状态对象e作为参数传递给监听者队列中的每个监听者*/
((ModelChangedListener) l.elementAt(i)).modelChanged(e);
}
}
}