[关键字]:java,design pattern,设计模式,《Java与模式》学习,阎宏,Ph.D,Factory,观察者模式,observer,event,java自定义事件,java事件机制,事件模型,事件监听,订阅发布,事件消息
[环境]:StarUML5.0 + JDK6
[作者]:天堂露珠 (wintys@gmail.com
[正文]:
java_event     MyEventTest.java:
package wintys.event;
import javax.swing.event.EventListenerList;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
/**
* Java的事件机制/自定义事件.
运行结果:
do something interesting in source here.
listener detects [event]:wintys.event.MyEvent[source=wintys.event.MySource@18158
59] [occur at]:2009-10-11 16:27:49
listener detects [event]:wintys.event.MyEvent[source=wintys.event.MySource@18158
59] [occur at]:2009-10-11 16:27:49
* @version 2009-10-11
* @author 天堂露珠 (wintys@gmail.com)
*/
class MyEventTest{
    public static void main(String[] args){
        MySource source = new MySource();
        MyListener myListener = new MyListener(){
            public void doMyAction(MyEvent e){
                System.out.println("listener detects " + e);
            }
        };
        source.addMyListener(myListener);
        source.addMyListener(myListener);
        source.addMyListener(myListener);
        source.removeMyListener(myListener);
        source.doSomething();
    }
}
/**
* 自定义的事件.
* @version 2009-10-11
* @author 天堂露珠(wintys@gmail.com)
*/
class MyEvent extends java.util.EventObject{
    private Date date;//记录事件发生的时间
    public MyEvent(Object source , Date date){
        super(source);
        this.date = date;
    }
    public String toString(){
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dt = df.format(date);
        return "[event]:" + super.toString() + " [occur at]:" + dt;
    }
}
/**
* 自定义事件监听器接口.
* @version 2009-10-11
* @author 天堂露珠(wintys@gmail.com)
*/
interface MyListener extends java.util.EventListener{
    void doMyAction(MyEvent e);
}
/**
* 自定义事件源.
* @version 2009-10-11
* @author 天堂露珠(wintys@gmail.com)
*/
class MySource{
    /**
     * 保存注册的监听器列表.
     * 子类可以使用它保存自己的事件监听器(非MyListener监听器)列表.
     */
    protected EventListenerList listenerList = new EventListenerList();
    private MyEvent myEvent = null;//fireDoMyAction()使用此变量
    /**
     * 没有做任何事
     */
    public MySource(){
    }
    /**
     * 添加一个MyListener监听器
     */
    public void addMyListener(MyListener listener){
        listenerList.add(MyListener.class , listener);
    }
    /**
     * 移除一个已注册的MyListener监听器.
     * 如果监听器列表中已有相同的监听器listener1、listener2,
     * 并且listener1==listener2,
     * 那么只移除最近注册的一个监听器。
     */
    public void removeMyListener(MyListener listener){
        listenerList.remove(MyListener.class , listener);
    }
    /**
     * @return 在此对象上监听的所有MyListener类型的监听器
     */
    public MyListener[] getMyListeners(){
        return (MyListener[])listenerList.getListeners(MyListener.class);
    }
    //Winty:Copy directly from javax.swing.event.EventListenerList
    /*Notify all listeners that have registered interest for
       notification on this event type.  The event instance
       is lazily created using the parameters passed into
       the fire method.
     */
    protected void fireDoMyAction() {
         // getListenerList() Guaranteed to return a non-null array
         Object[] listeners = listenerList.getListenerList();
         // Process the listeners last to first, notifying
         // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==MyListener.class) {
                // Lazily create the event:
                if (myEvent == null)
                    myEvent = new MyEvent(this , new Date());
                ((MyListener)listeners[i+1]).doMyAction(myEvent);
            }
        }
    }
    /**
     * 做一些事件源应该做的有意义的事,然后通知监听器.
     * 这里只是一个示例方法.
     * 例如:MySource如果是一个按钮,则doSomething()就可以命名为click(),
     * 当用户点击按钮时调用click()方法.
     */
    public void doSomething() {
        System.out.println("do something interesting here.");
        fireDoMyAction();//通知监听器
    }
}
    EventListenerList是特别需要说明的,它内部使用一个Object数组存放监听器。但是它并不是直接存放,而是先存监听器的class类型,然后再存监听器本身。即存放(MyListener.class , myListener)。一个Object数组可以存放多种类型的Listener,如果还有一种监听器AnotherListener,那么(AnotherListener.class , anotherListener)也可以存放。无论增删都是两个对象一同被添加或删除。上述代码中的listenerList.add(MyListener.class , listener)或listenerList.remove(MyListener.class , listener),以及fireDoMyAction()中的"i-=2",就是这样操作的。

[参考资料]:
  [2] 《Use EventListenerList to store event listener list》 : http://www.java2s.com/Code/Java/Swing-JFC/UseEventListenerListtostoreeventlistenerlist.htm
  [4] 《java 自定义事件的触发及监听》 : http://zhidao.baidu.com/question/50126506.html
  [5] 《关于观察者模式的问题》 : http://www.javaeye.com/topic/182643
  [6]  JDK1.6源代码
  [7] 《Java与模式》 : 阎宏
 
[附件]:
event_MyEventTest.zip:
EventListener.java
EventListenerList.java
EventObject.java
MyEventTest.java