「老话模式」事件监听模式(一)
事件监听模式,广泛的存在GUI,Servet,Spring等等地方。举一个简单的例子:
在Servet中,为我们提供了ServletContextListener接口,用于监听ServletContext上下文的创建事件。一旦Servlet上下文创建成功,Servlet容器就会对外抛出一个事件对象,告知所有注册在Servlet容器中的用于监听ServletContext相关事件的监听器。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); }}
例如上面代码,是经典的Spring容器加载的监听器,就是实现了ServletContextListener,要让监听器起作用,只需要配置在web.xml中即可:
这一步就相当于在注册监听器。
Java中的事件监听模式
那么事件监听模式到底是怎么样的呢?在java.util包中,其实已经为我们做好了事件监听模式的相关一些支持。
package java.util;/***事件监听器,用于监听某个事件的改变,作为一个标记接口*/public interface EventListener {}
这是第一个接口,用于标记事件监听器,类似观察者模式中的Observer接口,只是其中并没有定义任何方法,这仅仅只是一个标记接口。
package java.util;/*** 事件对象,用于在监听器和被监听对象之间传递信息*/public class EventObject implements java.io.Serializable { private static final long serialVersionUID = 5516075349620653480L; /** * 标记事件源对象,代表这个事件最开始是由谁抛出来的。 */ 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 + "]"; }}
这个类定义了一个事件对象,这个事件对象用于在被监听者和监听器之间传递数据。我们可以这样理解EventObject:
这个是观察者模式的类图,在观察者模式中,当主题状态变化的时候,由Subject调用notifyChange方法,在该方法中,依次调用Observer的update方法,而update方法中的第一个参数,Subject就是主题对象,也就可以理解为这里面的事件对象的事件源。意思就是之前是直接调用方法,而这里变成了传递一个事件对象。
我们可以来看看JAVA中自己对事件监听的使用模式。我们使用AWT中的例子来说明(没有用过也无所谓,看懂代码就行)。
package java.awt.event;public interface ActionListener extends EventListener { /** * Invoked when an action occurs. */ public void actionPerformed(ActionEvent e);}
首先来看awt中事件监听器的基础接口,ActionListener,在这里定义了actionPerformed方法,这个方法就是监听器监听到某个事件之后要做的逻辑。该方法传入一个ActionEvent对象,我们来看看这个事件对象的代码:
package java.awt;public abstract class AWTEvent extends EventObject { protected int id; protected boolean consumed = false; public AWTEvent(Event event) { this(event.target, event.id); } public AWTEvent(Object source, int id) { super(source); this.id = id; //... }}
首先我们看到的是AWTEvent类,这个类是ActionEvent的基类。可以看到这个类继承了EventObject,并且在这个类中包含了一些自己的属性(状态),比如id(简单理解为事件序列号),consumed(是否被处理)等;关键的,在构造方法中,对事件对象本身拥有的属性做了初始化操作。
package java.awt.event;public class ActionEvent extends AWTEvent { String actionCommand; long when; int modifiers; public ActionEvent(Object source, int id, String command) { this(source, id, command, 0); } public ActionEvent(Object source, int id, String command, int modifiers) { this(source, id, command, 0, modifiers); } public ActionEvent(Object source, int id, String command, long when, int modifiers) { super(source, id); this.actionCommand = command; this.when = when; this.modifiers = modifiers; }}
可以看到,ActionEvent其实和AWTEvent结构是一模一样的,只是他又多了几个这个事件相关的属性。并且同样也在构造器中进行了初始化操作。
事件相关的基础类看完了,我们来看下事件源,就是我们的主题对象,在swing中,有一个基础的类:组件(JComponent)定义了基础的组件,比如按钮,等;
public abstract class JComponent extends Container implements Serializable, TransferHandler.HasGetTransferHandler{ protected EventListenerList listenerList = new EventListenerList(); //其他的内容}
我只列出了事件相关的主要代码,可以看到,在JComponent中,定义了一个EventListenerList对象,这个对象又是一个什么?
public class EventListenerList implements Serializable { protected transient Object[] listenerList = NULL_ARRAY; public synchronized
我仅仅也只列出了其中一点关键代码,可以看到其实EventListenerList里面维护了一个数组,而数组里面存放的就是监听器类型和对应的监听器实例,一组一组存放(在add方法中可以看到);
到具体的组件,比如按钮:
public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants { //添加action监听器 public void addActionListener(ActionListener l) { listenerList.add(ActionListener.class, l); } //移除action监听器 public void removeActionListener(ActionListener l) { if ((l != null) && (getAction() == l)) { setAction(null); } else { listenerList.remove(ActionListener.class, l); } } protected void fireActionPerformed(ActionEvent event) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); ActionEvent e = null; // 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]==ActionListener.class) { // Lazily create the event: if (e == null) { String actionCommand = event.getActionCommand(); if(actionCommand == null) { actionCommand = getActionCommand(); } e = new ActionEvent(AbstractButton.this, ActionEvent.ACTION_PERFORMED, actionCommand, event.getWhen(), event.getModifiers()); } ((ActionListener)listeners[i+1]).actionPerformed(e); } } } //其他代码}
同样简化的代码,可以看到这里的处理方式。
按钮基类(AbstractButton)继承JComponent,所以他里面也维护一个EventListenerList;AbstractButton中定义了添加和移除ActionListener的代码;当一个action施加在按钮上,会调用fireActionPerformed方法来处理,可以看到,该方法得到按钮上绑定的所有监听器,并筛选出监听ActionEvent的ActionListener,然后创建一个ActionEvent,并触发每一个ActionListener的actionPerformed方法,完成监听器的调用。
我们简单画一个图来说明这个过程:
这是三个组件之间的关系
下面是事件触发的说明图:
整个流程非常清晰了。
到这里,事件监听模式在Java中的基本应用已经阐述,具体Java是怎么支持事件监听模式的,又应该怎么使用,后文再介绍。
作者:叩丁狼教育stef
查看原文 >>