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);
}
}
}