一、Java事件主要角色
Source:事件源,即触发事件的对象;
EventObject:事件对象,即带有 EventSource 信息的事件对象,是对EventSource的包装;
Eventlistener:事件监听器,对该事件的处理。
说明:
1、Source:即任何具有行为的Java 对象,具有行为是为了能触发事件。
2、EventObject类:
继承关系:直接继承于Object ,实现了Serializable接口,是所有事件对象的父类(如界面编程中Action事件类的父类AWTEvent)。定义于java.util包。
EventObject提供了3个基本方法:
构造方法:EventObject(Object source)//source不能为null
普通方法:
Object getSource();//返回事件源
String toString();//返回该事件类名与事件源名
3、EventListener 接口:
继承关系:直接继承于Object,所有监听器的父接口。定义于java.util包。无方法。
二、Java 事件实现过程与触发过程
实现过程:
首先,为要触发事件的对象(source)定义事件对象;
其次,为事件对象定义事件监听器;
最后,定义事件源(source)的类,指定添加监听器的方法。
触发过程:
以按钮点击事件(属于Action事件)为例,首先我们通过继承于组件的addActionListener 方法,为按钮添加了ActionListener 接口实现类,那么按钮类中定义的与点击相关的某个方法A 就会把自身作为source创建一个事件对象,并将该事件对象传入addActionListener 方法所添加的监听器中,在监听器中根据source 类型或实例选择执行某段代码,如此便完成了事件的触发。
需要注意的是,组件(如JFram、Button等)触发Action 等事件时事件对象的创建是由JVM 自动完成的,而不是通过开发人员编码实现,即我们不关注Action 事件触发的细节,而是只关注 ActionListener 实现类中对业务的处理。
由此,可简单理解三个对象的作用为:
事件源(source):其一、规定事件由谁来产生;其二、在listener 中作路由;
事件对象(EventObject):起到传递事件信息的作用。是对source 的包装,根据不同的构造方式,可以附带除source外的其它信息;
事件监听器(EventListener):业务关注点。根据事件对象中的事件源(source)或事件对象类型进行路由选择,实现不同业务。
通过代码加深理解:
1、EventObject 构造函数解读:以ActionEvent 其中一个构造函数为例
public ActionEvent(Object source, int id, String command, int modifiers) {
this(source, id, command, 0, modifiers);
}
最终其实调用了另外一个构造函数,可以看出ActionEvent 中多了事件id、事件相关的字符串command(对输入框此值即是输入的值)、modifiers(代表事件期间是否按下shift, ctrl, alt, meta),此处0其实代表了一个long类型参数,代表修改时间。可见,ActionListener 在EventLIstener 基础上添加了除source 外的更多信息,可类比其他事件。
2、awt 或swing 界面编程中对事件的典型处理过程:以Button 的点击事件(属于ActionEvent)为例
首先,定义事件对象。此过程省略,java 自动实现了。
其次,定义监听器。如下:
class MyActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){
//定义发生ActionEvent 事件时要执行什么。当然,一个监听器是可以监听多个事件源触发的ActionListener的,所以通常都会先判断事件源
if(e.getSource() == myButtonOne){...}
else if(e.getSource()==myButtonTwo{...}
...
}
}
说明:ActionEvent 不是我们自己定义的,所以不知道点击按钮时,按钮对象产生该ActionEvent 对象的细节,当然可读源码。如果是自己定义一些事件与事件源,则在事件源内部何时产生事件对象及怎样产生事件对象完全由我们自己定义。上面代码是通过==判断事件源的,这要求该类能直接访问到事件源对象。监听器的函数参数也有文章可做,可通过IoC的思想,传入实现监听器接口的不同类,通过类的不同来处理不同业务。
if(e instanceof MyEvent)//不关注事件源,而关注事件类型时,通过事件类型判断
if(e.getSource() instanceof MyEventSource)//类型已确定,关注事件来源时,通过事件源类型判断
最后,注册监听器。
JButton myButtonOne=new JButton("按钮一");
JButton myButtonTwo=new JButton("按钮二");
MyActionListener listener=new MyActionListener();
myButtonOne.addActionListener(listener);
myButtonTwo.addActionListener(listerner);
监听器理解:监听器本身就是实现了监听接口,所以其本质作用于接口相同,即将我们的关注的业务从事件源中抽离出来,便于管理。对于多个事件源的监听器,每次都只是根据事件源或事件类型执行了对应的那段代码。如果对addActionListener 后事件具体怎么产生的还是有疑问,那就看看第三点!
三、编写自己的事件模拟Java 事件处理
目的:了解事件是怎么自动触发的(事件源类是关键)。
代码:
/**
* 事件类。参数source起路由作用,判断事件来源,作用相当于JSP
* 的servlet。此处定义了两个事件源,实际第二个没用到,只是想说明可以通过注入接口或超类的思想将事件类别作为判断依据。
*/
class EventClassOne extends EventObject{
public EventClassOne(Object source) {
/*选择执行哪中构造方式,此处只有EventObject(Object source)一种*/
super(source);
}
}
class EventClassTwo extends EventObject{
public EventClassTwo(Object source) {
super(source);
}
}
/**事件源类。表明谁触发了事件,用于作为EventObject类的构造参数,在listener中作路由*/
class EventSource{
private String who;
Vector listeners=new Vector();
public EventSource(String who){
this.who=who;
}
public String getActioner(){
return who;
}
public void addMyEventListener(MyEventListener listener){
listeners.add(listener);
}
/*设定say方法能被MyEventListener对象监听到*/
public void say(String words){
System.out.println(this.getActioner()+"说:"+words);
for(int i=0;i<listeners.size();i++){
MyEventListener listener=(MyEventListener) listeners.elementAt(i);
/*发布事件。当然应该事先规划say方法事件能发布给哪些事件监听器。*/
listener.onMyEvent(new EventClassOne(this));
}
}
}
/**
* 自定义监听器中通过EventObject判断事件来源,所以前面说EventObject是起路由功能。
*/
class MyEventListener implements EventListener{
/*EventListener是与EventObject同级的最原始的监听器,当然里面什么方法都没有*/
public void onMyEvent(EventObject e){
/*如果该类与EventObject实例处于同一个类中,可以直接使用==判断事件来源*/
if(e.getSource() instanceof EventSource){
/*事件来源于OtherSource时要处理的业务*/
EventSource tempSrc=(EventSource)e.getSource();
System.out.println("收到来自"+tempSrc.getActioner()+"的事件!");
}
/*else if(e.getSource() instanceof OtherSource){
System.out.println("事件来源于OtherSource时要处理的业务");
}*/
}
}
/**
* 关键点:new几个事件,在awt、swing等界面编程中,ActionEvent事件(即行为)
* 事件是java已经实现的,如点击鼠标事件,所以组件A只需使用addXxxListener方法
* 添加监听器即可,运行时用户点击组件A,则自动使用A产生一个相应事件,并执行所有
* listener的actionPerformed方法处理事件业务。*/
public class Main{
public static MyEventListener listener = null;
public static void main(String[] args){
listener = new MyEventListener();
EventSource 小白 = new EventSource("小白");
小白.addMyEventListener(listener);
小白.say("今天天气不错");
小白.say("适合出去走走");
}
}
运行结果:
特别说明:
instanceof 操作符能辨别子类,如上面代码中MyEventListener 中原来为:
if(e.getSource() instanceof EventSource)
改为下列通过类判断后同样能得到正确结果:
if(e instanceof EventClassOne)
四、Spring 中事件机制
原理都是一样的,见另外一篇日志