Java Swing中的事件机制和观察者模式
Java Swing简述
Java Swing是Java 提供的一套关于用户图形界面(GUI)编程的工具包,现在已经是Java基础类的一部分。其中包含图形化界面编程常用的要素,例如:容器、组件(文本框、按钮、下拉菜单、表等)、布局等等。相比与传统的Java AWT,Java Swing拥有更好的平台移植性。
作为一款GUI编程工具包,Java Swing或许现在远不如QT、MFC、HTML5等技术流行,但是在其中所包含的事件处理却是几乎所有GUI编程中所共有的技术。因为GUI工具包不仅需要提供优美的图形化组件,同时还需要能够与用户进行交互,而交互的核心就是事件处理
事件处理机制
事件处理机制是一种事件处理框架,其设计目的是把GUI交互动作(单击、菜单选择等)转变为调用相关的事件处理程序进行处理。JDK 1.1以后Java采取了授权处理机制(Delegation—based Model),事件源可以把在其自身所有可能发生的事件分别授权给不同的事件处理者来处理。
------百度百科
事件机制的三个基本要素
-
事件(event) 通常是指某种类型的操作。例如单击了一次Button或者文本框中输入了一个字符,则单击、输入字符 就是事件
-
事件源(Event Source):可以简单的将其理解为事件发生的源头,例如单击了一次Button,由于单击是在Button上发生的,所以Button就是事件发生源头,即事件源。在Swing中,通常包含了所有能交互的的组件
-
事件监听器(Listener):是事件处理机制的核心,定义了所有事件处理的相关逻辑。事件监听器的主要作用,是关注可能发生事件的对象(事件源),并在其发生了特定的事件后,能对其做出反应
事件监听机制与观察者模式之间的联系
从事件监听器(Listener)的作用上分析,监听器的功能就是能够感知到对象上发生了某个事件。换句话说,就是当事件源上发生了某个事件时,监听器希望能被通知到,并且在得到通知后能对其做出相应的处理。而这实际上就是观察者模式中观察者所希望的事情。因此,我们可以很自然将监听者映射到观察者模式中观察者(Observer),那么事件源和事件就相应的映射到了观察者模式中的目标(Subject)。
下面就从源码上验证上述结论:
Subject的一些必要条件
-
知道他的观察者。(维护观察者列表)
-
提供注册和删除观察者的接口
-
当状态发生变化时,向观察者发送通知
Observer的一些必要条件
-
当目标发生变化时,提供一个更新的接口(回调函数)
Swing中的组件类(等价于观察者中的Subject)
组件类在监听机制中就是事件源
(由于代码过长,只截取重要部分)
所有组件的父类JComponent
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public abstract class JComponent extends Container implements Serializable, TransferHandler.HasGetTransferHandler { ... /** A list of event listeners for this component. */ // 维护了观察者的列表 protected EventListenerList listenerList = new EventListenerList(); ... }
该类中定义了,所有组件类所需要维护的监听器列表
某个相对具体组件AbstractButton
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public abstract class AbstractButton extends JComponent implements ItemSelectable, SwingConstants { ... //对于观察者的管理 start// public void addActionListener(ActionListener l) { listenerList.add(ActionListener.class, l); } public void removeActionListener(ActionListener l) { if ((l != null) && (getAction() == l)) { setAction(null); } else { listenerList.remove(ActionListener.class, l); } } public ActionListener[] getActionListeners() { return listenerList.getListeners(ActionListener.class); } //对于观察者的管理 end// //向观察者发送通知 start// 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); } } } //向观察者发送通知 end// ... }
Swing中的监听器类(等价于观察者中的Observer)
(由于代码过长,只截取重要部分)
监听器类就是事件机制中的事件监听器
所有监听器的父类EventListener
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public interface EventListener { }
该类并未定义任何方法,实际上他的作用只是一个标记类,用于身份的说明;
某个相对具体监听器ActionListener
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public interface ActionListener extends EventListener { public void actionPerformed(ActionEvent e); }
目前为止,我已经简单地阐述了事件监听机制中【事件源(Event Source)、监听者(Listener)】和观察者模式中【目标(Subject),观察者(Observer)】之间的映射关系。似乎还少了一个重要地角色事件(event)。下面贴出event的源码
Swing中的事件类
所有事件类的父类EventObject
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class EventObject implements java.io.Serializable { ... protected transient Object source; public Object getSource() { return source; } ... }
某个具体的事件类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class ActionEvent extends AWTEvent { ... String actionCommand; int modifiers; public String getActionCommand() { return actionCommand; } public long getWhen() { return when; } ... }
仅从代码上看,event只是在事件源上做了一层封装,同时保存一些事件的状态信息,然后回传给了监听器的回调函数,这样监听器中的更新方法就可以获得了事件源,以及事件相关的信息。从观察者的角度上说,就是观察者获得了目标的相关状态。因此,在这里我可以将事件监听机制中(事件源和事件)两者同时映射到观察者模式中的目标(Subject) 上
实际上event做的事情不止这些,他是事件发起者和事件接收者之间的桥梁。从观察者模式的角度上说,就是触发者与观察者之间的桥梁。(触发者:改变目标(subject)状态的对象)。
在事件监听机制中,监听器一般不会主动去触发一些事件(这一点与观察者不同,在观察者设计模式中,观察者可以直接改变目标的状态)。事件往往是由系统触发的,例如点击事件,敲击键盘的事件等等。监听者往往作为一个被动通知的对象。系统会实时捕捉一些事件,然后将其放到一个事件队列当中,然后Swing会单独开启一个线程,从事件队列中获得事件,然后对事件进行分发,最终通过事件中维护的事件源通知监听者。
java swing中的源码:
事件分发的线程:EventDispatchThread
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class EventDispatchThread extends Thread { ... public void run() { try { pumpEvents(new Conditional() { public boolean evaluate() { return true; } }); } finally { getEventQueue().detachDispatchThread(this); } } ... }
从pumpEvents一路追源码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class EventDispatchThread extends Thread { void pumpOneEventForFilters(int id) { ... eq.dispatchEvent(event); ... } } public class EventQueue { ... private EventDispatchThread dispatchThread; //事件分发 protected void dispatchEvent(final AWTEvent event) { ... dispatchEventImpl(event, src); ... }
从dispatchEventImpl继续一路追:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private void dispatchEventImpl(final AWTEvent event, final Object src) { event.isPosted = true; ... if (event instanceof ActiveEvent) { // This could become the sole method of dispatching in time. setCurrentEventAndMostRecentTimeImpl(event); ((ActiveEvent)event).dispatch(); } else if (src instanceof Component) { ((Component)src).dispatchEvent(event); event.dispatched(); } ... }
最后获得Component 对象,再往下就是获得Listhenerlist,然后逐个调用
观察者模式的总结
1)观察者模式是一种反向通知的机制,观察者需要知道目标(Subject)的状态是否发生了变化。但是感知目标(Subject)状态变化,并不是由观察者不断地去询问来实现,而是利用回调函数地方法,将观察者注册到目标中去,通过目标(Subject)自身地notify的方法反向通知观察者。相比与观察者主动询问,回调的方式有更好的即时性。
2)天然地支持广播通信,目标(Subject)不需要指定它的接收者,而是简单地将信息发送出去,由观察者决定是否对通信进行处理。
3)将目标(Subject)和观察者进行分离/解耦: 一方面目标(Subject)不需要知道观察者,它所要做地仅仅是将自己的变化,通过观察者提供的方法,发送给观察者。不需要在意观察者的逻辑。而观察者也不需要知道目标(Subject)的实现,它只需要在意当变化发生时,自己需要处理的逻辑。