本文同时发布于个人网站 https://ifuyao.com/blog/java-event/

相信做 Java 开发的朋友,大多都是学习过或至少了解过 Java GUI 编程的,其中有大量的事件和控件的绑定,当我们需要在点击某个按钮实现某些操作的时候,其实就是为这个按钮控件注册了一个合理处理点击事件的监听器。此外,Spring Framework 中也有许多用到事件处理机制的地方,如 ApplicationContextEvent 及其子类,代表着容器的启动、停止、关闭、刷新等事件。本文会为大家介绍 Java 的事件处理机制,也会用示例来说明,如何优雅地触发并处理一个自定义事件。

委托事件模型

Java 自 1.1 之后基于委托事件模型,定义了标准一致的获取和处理事件的方式。它的思路非常简单,由事件源发起特定事件,并将事件发送给一个或多个事件监听器,而监听器在此过程中一直处于等待状态,直到接收到该事件,然后处理事件并返回。实现起来也很简单:

  • 定义事件
  • 实现特定的监听器接口,接收特定类型的事件
  • 实现代码,注册(或解除)监听器作为特定事件类型的接收者
  • 在恰当的时机触发事件

核心组件

在 Java 的这个事件处理机制中,包含三个核心组件:

  1. 事件 事件对象,描述相位的变化。比如在 GUI 中一个动作的点击,在 Spring Framework 中容器的启停,更多的诸如电脑的开机、关机、休眠,缓存的过期,公众号的关注、取关等等。

  2. 事件源 可以是任意对象,它具备触发事件的能力。一般在这个对象中注册(或解除)监听器,此外事件的触发通常也在这里。一个源可能产生多个不同类的事件,要为不同的事件类型分别注册事件监听器,而每个事件类型可以注册一个或多个监听器。

  3. 事件监听器 一个实现了特定接口的类,它需要实现对针对特定事件的具体处理方法,且必须被注册到该特定事件上。

那么问题来了,我们如何优雅地触发并处理一个自定义事件呢?

自定义事件

在 Java 中自定义事件非常简单。考虑到现在各个应用中都有绑定社交账号的需求,我们就以此为例,在社交账号绑定或者解绑时简单的打印一条记录。

首先定义一个事件对象,代码如下:

public class SocialEvent extends EventObject {
private static final long serialVersionUID = -5473622737706032666L; public static final int WECHAT_BIND = 1;
public static final int WECHAT_UNBIND = 2;
public static final int WEIBO_BIND = 3;
public static final int WEIBO_UNBIND = 4; private int socialType; public SocialEvent(Object source, int socialType) {
super(source);
this.socialType = socialType;
} public int getSocialType() {
return socialType;
} public void setSocialType(int socialType) {
this.socialType = socialType;
}
}

事件类必须是 EventObject 的子类。值得一提的是,事件对象通常代表一类而非一个事件,即合理的做法是将一类事件而非一个事件概念融合起来。

接下来,我们实现一套事件处理逻辑,即事件监听器:

public class SocialEventListener implements EventListener {
public void onSocialChanged(SocialEvent event) {
switch (event.getSocialType()) {
case SocialEvent.WECHAT_BIND:
System.out.println("Wechat bind.");
break;
case SocialEvent.WECHAT_UNBIND:
System.out.println("Wechat unbind.");
break;
case SocialEvent.WEIBO_BIND:
System.out.println("Weibo bind.");
break;
case SocialEvent.WEIBO_UNBIND:
System.out.println("Weibo unbind.");
break;
default:
System.out.println("Bad social type.");
break;
}
}
}

此外,我们需要一个事件源:

public class Social {

    private List<SocialEventListener> listeners;

    public void addListener(SocialEventListener listener) {
if (listeners == null) {
listeners = new ArrayList<>();
}
listeners.add(listener);
} public void removeListener(SocialEventListener listener) {
if (listeners != null) {
listeners.remove(listener);
}
} public void emitEvent(SocialEvent event) {
for (SocialEventListener listener : listeners) {
listener.onSocialChanged(event);
}
} public List<SocialEventListener> getListeners() {
return listeners;
} public void setListeners(List<SocialEventListener> listeners) {
this.listeners = listeners;
}
}

在这里,我们定义了专门的类 Social 作为事件源,事实上,可以在任意其他的类中实现事件的触发与注册逻辑,比如启动类中。

顺便说一句,我们在 Java GUI 编程中,通常只需要为组件注册事件监听器,而无需考虑事件的触发逻辑,这是因为它们的事件是由系统自动触发的。

具体的代码实现细节,请参考源码 Demo Source Code

一文学会Java事件机制的更多相关文章

  1. Java 事件机制

    java事件机制包括三个部分:事件.事件监听器.事件源. 1.事件.一般继承自java.util.EventObject类,封装了事件源对象及跟事件相关的信息,用于listener的相应的方法之中,作 ...

  2. 【React】354- 一文吃透 React 事件机制原理

    大纲 主要分为4大块儿,主要是结合源码对 react事件机制的原理 进行分析,希望可以让你对 react事件机制有更清晰的认识和理解. 当然肯定会存在一些表述不清或者理解不够标准的地方,还请各位大神. ...

  3. 大牛带你学会java类加载机制,不要错过,值得收藏!

    很多人对java类加载机制都是非常抗拒的,因为这个太难理解了,但是我们作为一名优秀的java工程师,还是要把java类加载机制研究和学习明白的,因为这对于我们在以后的工作中有很大的帮助,因为它在jav ...

  4. 一文了解java异常机制

    1.异常的概述 1.1什么是异常? 异常:程序在运行过程中发生由于外部问题导致的程序异常事件,发生的异常会中断程序的运行.(在Java等面向对象的编程语言中)异常本身是一个对象,产生异常就是产生了一个 ...

  5. 一文学会Java的交互式编程环境jshell

    什么是交互式编程环境?重点词交互,在这样的编程环境中,你每输入一行代码,环境都会给你一个反馈,这就是交互式的编程环境.这种编程环境并不太适合工程化的复杂性需求,但在一些快速验证.简单计算之类的场景下还 ...

  6. 一文学会Java死锁和CPU 100% 问题的排查技巧

    做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开 工欲善其事,必先利其器 00 本文简介 作为一名搞技术的程序猿或者是攻城狮,想必你应该是对下面这两个问题有所了解,说不定你在 ...

  7. 【性能优化】一文学会Java死锁和CPU100%问题的排查技巧

    原文链接: 00 本文简介 作为一名搞技术的程序猿或者是攻城狮,想必你应该是对下面这两个问题有所了解,说不定你在实际的工作或者面试就有遇到过: 第一个问题:Java死锁如何排查和解决? 第二个问题:服 ...

  8. JAVA事件监听机制学习

    //事件监听机制 import java.awt.*; import java.awt.event.*; public class TestEvent { public static void mai ...

  9. vb和vb.net事件机制

    学习java事件前,回顾了下vb6和vb.net的事件机制,总结在这里,供对比用. 事件是面对对象中对象间通信的方法.事件发生者(又叫事件源)发生一个事件时,通过发送一条消息,给事件接受者(事件处理者 ...

随机推荐

  1. 关于Ubuntu18.04上Python版本管理

    时间: 2019-11-11 整理: pangyuaner 标题:树梅派上多版本python及pip安装使用管理指南 地址:https://blog.csdn.net/zbgjhy88/article ...

  2. C++回调机制

    一直对回调机制不是很了解,今天索性搜了很多资料顺便整理一下,进步一点点. 1.Callback方式(回调函数) 什么是回调函数? 简而言之,回调函数就是一个通过函数指针调用的函数.如果你把函数的指针( ...

  3. 小程序使用 lodash 的问题

    import _ from 'lodash' 报错: vendor.js:11874 Uncaught TypeError: Cannot read property 'prototype' of u ...

  4. WebService学习总结(四)--基于CXF的服务端开发

    本节将实践目前最流行的第二种web service 发布和调试框架  CXF Apache CXF 是一个开放源代码框架,提供了用于方便地构建和开发 Web 服务的可靠基础架构.它允许创建高性能和可扩 ...

  5. openswan发送状态机分析

    openswan发送状态机分析 1. 函数调用关系 2. 函数说明 如果按用户空间.内核空间划分的话,此部分代码更多是运行在内核空间的. 2.1 ipsec_tunnel_init_devices() ...

  6. Spring Boot 2.x 之 Spring Data JPA, Hibernate 5

    1. Spring Boot常用配置项 基于Spring Boot 2.0.6.RELEASE 1.1 配置属性类 spring.jpa前缀的相关配置项定义在JpaProperties类中, 1.2 ...

  7. ORACLE 12C Identity Column(身份列) 实现自增长字段

    Oracle 12c提供的Identity Column特性简化了自增字段的定义. 声明自增字段通常有3种常见的用法,以下三种方式都支持INSERT语句中省略自增字段的插入,但有些许差别. 1. GE ...

  8. php 开启报错

    // 开启报错提醒ini_set("display_errors", "On");error_reporting(E_ALL | E_STRICT); // 某 ...

  9. DISCUZ论坛添加页头及页尾背景图片的几种方法

    先给大家分享页头添加背景图片的两种方法:1. 第一种效果,是给discuz的整体框架添加背景图片,见图示: 添加方法如下:找到你现在使用模板common文件下的header.html文件,在<h ...

  10. GCN数据集Cora、Citeseer、Pubmed文件分析

    简介 本文将对Cora.Citeseer.Pubmed 数据集进行详细介绍 Cora.Citeseer.Pubmed 数据集 来源 图 节点 边 特征 标签(y) Cora "Collect ...