事件监听模式,广泛的存在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中即可:

contextLoaderListener org.springframework.web.context.ContextLoaderListener

这一步就相当于在注册监听器。

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 void add(Class t, T l) { if (!t.isInstance(l)) { throw new IllegalArgumentException("Listener " + l + " is not of type " + t); } if (listenerList == NULL_ARRAY) { listenerList = new Object[] { t, l }; } else { int i = listenerList.length; Object[] tmp = new Object[i+2]; System.arraycopy(listenerList, 0, tmp, 0, i);​ tmp[i] = t; tmp[i+1] = l;​ listenerList = tmp; } }​ //其他代码}

我仅仅也只列出了其中一点关键代码,可以看到其实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

查看原文 >>
相关文章