当前位置:网站首页>聊聊事件监听那些事-上
聊聊事件监听那些事-上
2022-06-12 15:41:00 【下岗码农大飞】
前段时间因为工作的需要用到Spring监听事件,翻翻文档将功能实现了,但是存在少许理解不畅的地方,今天有空来梳理梳理。
需求背景
叶子同学在新入职公司,老大让他实现登陆功能,叶子随手写完,上线无bug,一切安好
//登陆伪代码
public void login(....){
userLogin(....);
}几天之后,老大说为维护用户的粘度,每天登陆送积分。叶子同学,二话不说,一顿操作后,上线无bug,一切安好
//登陆伪代码
public void login(....){
//登陆
userLogin(....);
//送积分
loginPoint(....)
}又几天后,老大说,为了客户安全,每次异地登陆发送邮件。叶子同学稍微抱怨,看在钱份上又是一顿操作后,上线无bug, 一切安好
//登陆伪代码
public void login(....){
//登陆
userLogin(....);
//送积分
loginPoint(....)
//发送邮件
sendEmail(....)
}又又几天后,老大说,部分客户不用邮件,用短信。叶子同学压着怒气,看着银行卡,又是一顿操作后,上线无bug, 一切安好
//登陆伪代码
public void login(....){
//登陆
userLogin(....);
//送积分
loginPoint(....)
//发送邮件
sendEmail(....)
//发短信
sendSms(...)
}又又又几天后,老大还没开口,叶子同学就忍无可忍啦,得加钱。老大哄了好久,说不改需求了。改bug,用户抱怨登陆慢,有时还不成功。叶子二话不说,接手排查,查出问题啦
1> 邮件发送耗时
2>同步实现,如果邮件发送超时,登陆会出异常
代码改进
//登陆伪代码
public void login(....){
//登陆
userLogin(....);
try{
//送积分
new Thread(()->loginPoint(....)).start();
//发送邮件
new Thread(()->sendEmail(....)).start();
//发短信
new Thread(()->sendSms(....)).start();
}catch(Exception e){
//异常处理
}
}问题解决,功能实现,ok~
又又又又几天之后,老大说,只需要实现登陆功能即可,其他都不要~
叶子同学一句卧槽,然后是一段含母非常高的国粹。
此时,问:如果你是叶子同学,你有啥方案能优雅应对上面的需求变更呢?
一种优雅方案:事件监听机制
事件概念
定义
事件监听机制:就是对一个事件(行为动作)进行监听,当外界触发某事件时,监听程序马上被捕获该事件,并触发相应的响应,这过程称之为事件监听机制。
组成
事件监听机制有3个核心组成部分:
1>事件,标记某种行为动作,比如:鼠标点击事件,鼠标移动事件等。
2>事件源,被监控的对象或组件,事件发生的地方。比如:点击按钮,触发点击事件,按钮就是实现源。
3>事件监听器,监听事件的操作类,一旦事件发生(被触发),则执行事件监听器预设的逻辑,进行事件响应。

1>定义事件,并绑定到事件源中
2>定义事件监听器,监听事件源
3,4>用户某行为触发事件
5>事件监听器监控到事件发送,执行事件响应逻辑。
以上面的登录为例:
事件源:login方法
事件:用户login行为
事件监听器:此处没有,需要额外定制,但是事件响应:送积分,发邮件,发短信。
事件实现
以登录为例子实现事件监听机制
1>定义抽象事件
/**
* 抽象事件类
* 作用:定制事件逻辑
*/
public class AbstractEvent {
//绑定的事件源
private Object source;
public AbstractEvent(Object source) {
this.source = source;
}
public Object getSource() {
return source;
}
public void setSource(Object source) {
this.source = source;
}
}2>定制登陆事件
/**
* 登陆事件
*/
public class LoginEvent extends AbstractEvent{
public LoginEvent(Object source) {
super(source);
}
}3>定义事件监听器
/**
* 事件监听器
* 作用:当监控的事件发送时,执行预设的逻辑
*/
public interface EventListener<E extends AbstractEvent> {
/**
* 预设逻辑方法
* 事件被触发,马上执行
*/
void onEvent(E event);
}4>定制登陆事件监听器
积分监听器
/**
* 加积分监听器器:
* 当用户登陆事件触发后,马上执行
*/
public class PointsListener implements EventListener<LoginEvent> {
public void onEvent(LoginEvent event) {
System.out.println(event.getSource() + "发生后,执行积分+1操作");
System.out.println(Thread.currentThread().getName());
}
}
短信监听器
/**
* 加积分监听器器:
* 当用户登陆事件触发后,马上执行
*/
public class SmsListener implements EventListener<LoginEvent> {
public void onEvent(LoginEvent event) {
System.out.println(event.getSource() + "发生后,执行发送短信操作");
System.out.println(Thread.currentThread().getName());
}
}邮件监听器
/**
* 加积分监听器器:
* 当用户登陆事件触发后,马上执行
*/
public class EmailListener implements EventListener<LoginEvent> {
public void onEvent(LoginEvent event) {
try {
//模拟10s延时
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(event.getSource() + "发生后,执行发送邮件操作");
System.out.println(Thread.currentThread().getName());
}
}5>定义事件广播器
/**
* 事件广播器
* 1>注册事件监听器
* 2>删除事件监听器
* 3>事件触发时,广播事件
*/
public interface EventMulticaster {
//广播事件
void multicastEvent(AbstractEvent event);
//注册事件监听器
void registListener(EventListener listener);
//删除事件监听器
void removeListener(EventListener listener);
}6>定制简单的事件广播器
/**
* 事件广播器实现类
* 作用:维护事件监听器
*/
public class SimpleEventMulticaster implements EventMulticaster {
//key:事件字节码对象, value:当前事件绑定的事件监听器
private Map<Class<?>, List<EventListener>> map = new HashMap<Class<?>, List<EventListener>>();
public void multicastEvent(AbstractEvent event) {
List<EventListener> eventListeners = map.get(event.getClass());
if(eventListeners != null){
ExecutorService executorService = Executors.newCachedThreadPool();
for (EventListener eventListener : eventListeners) {
//异步
executorService.submit(()-> eventListener.onEvent(event));
//同步
//eventListener.onEvent(event);
}
executorService.shutdown();
}
}
public void registListener(EventListener listener) {
//获取监听器绑定的事件
ParameterizedType getType = (ParameterizedType)listener.getClass().getGenericInterfaces()[0];
Type type = getType.getActualTypeArguments()[0];
Class<?> clz = (Class<?>) type;
List<EventListener> listeners = map.get(clz);
if(listeners == null){
listeners = new ArrayList<EventListener>();
map.put(clz, listeners);
}
listeners.add(listener);
}
public void removeListener(EventListener listener) {
//获取监听器绑定的事件
ParameterizedType getType = (ParameterizedType)listener.getClass().getGenericInterfaces()[0];
Type type = getType.getActualTypeArguments()[0];
Class<?> clz = (Class<?>) type;
List<EventListener> listeners = map.get(clz);
if(listener != null){
listeners.remove(listener);
}
}
}7>综合测试
public class App {
//1:初始化事件广播器
public static SimpleEventMulticaster multicaster = new SimpleEventMulticaster();
static {
//2:注册监听器
//登陆事件上绑定3个监听器
multicaster.registListener(new PointsListener());
multicaster.registListener(new EmailListener());
multicaster.registListener(new SmsListener());
}
//3:模拟登陆
public static void login(){
//4:用户登陆成功触发登陆事件
System.out.println("用户执行登陆逻辑");
System.out.println(Thread.currentThread().getName());
//5:广播登陆事件
multicaster.multicastEvent(new LoginEvent("用户登陆啦"));
System.out.println("登陆成功.....");
}
public static void main(String[] args) {
App.login();
System.out.println(Thread.currentThread().getName());
}
}
时序图

后续
如果后续需求变更,只需要操作广播器对监听器进行增删处理即可。
边栏推荐
- Loadbalancer load balancer
- Preparation of service for robot moving forward and rotating
- Import and export steps of SQL Server 2008
- Tcp/ip three handshakes and four waves (interview questions)
- Microservice fault tolerance
- Module yaml error: Unexpected key in data: static_ context [line 9 col 3]
- Use of boost:: bind() in ROS
- Task fruit Juicer 0611
- CUDA out of memory or brokenpipeerror: [errno 32] broken pipe or oserror: [winerror 1455] solution to the problem that the page file is too small
- TS 22.011
猜你喜欢

Great God cracked the AMD k6-2+ processor 22 years ago and opened the hidden 128KB L2 cache

Deepin20.6 rtx3080 installer le lecteur de carte graphique 510.60.02, cuda 11.6, pytorch1.11

Tcp/ip three handshakes and four waves (interview questions)

Explanation of socket principle (where, what and how to use it)

Task output: dense snow ice city theme song 0612

Defer learning in golang

Interface.

Microservice fault tolerance

虚拟机中用户和root忘记密码解决办法

What is JUC in high concurrency programming
随机推荐
Jupyter notebook new environment shortcut
Understanding of Odom coordinate system
Servlet知识详解(2)
Servlet连接数据库实现用户登录功能
Some useful websites
Unity get local video / download network video
Idea pull branch code
jupyter notebook新環境快捷方式
Web UI automation test
C language partition bin file program
华为设备配置CE双归属
Increase the maximum number of MySQL connections
2022.02.28 - SX11-05. The largest rectangle in the histogram
What is JUC in high concurrency programming
Use of packet capturing tool Fiddler: simulating speed limit test process in weak network environment
任务 输出密雪冰城主题曲 0612
[jvm learning] types of GC and allocation process of objects on JVM heap
Change according to the situation, the road to promotion in the second half of 2022
How to analyze the running time and CPU utilization of Go programs?
Use and understanding of generics