Java 事件监听
下岗码农大飞 人气:7前言
前段时间因为工作的需要用到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>事件监听器监控到事件发送,执行事件响应逻辑。
以上面的登录为例:
- 事件源: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()); } }
时序图
加载全部内容