Java Spring 事件监听详情解析
作者:??下岗码农大飞???? 发布时间:2021-06-05 00:02:08
前言
前段时间因为工作的需要用到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());
}
}
时序图
来源:https://juejin.cn/post/7114669073320902669
猜你喜欢
- Java 8 中 Function 接口的介绍Java 8 中提供了一个函数式接口 Function,这个接口表示对一个参数做一些
- 什么是FTPFTP(File Transfer Protocol)是TCP/IP网络上两台计算机传送文件的协议,使得主机间可以共享文件.可以
- 目录场景介绍自动填充处理器Mybatis-Plus配置类配置实体类中相关字段的自动填充策略在阿里开发手册的建表规约中有说明,数据库表中应该都
- 我们做项目实际中经常会遇到这样的情况,创建一个common项目(Maven项目)作为公用项目,common中有很多工具类可以供其它多个项目调
- 简介我们在使用flutter的过程中,有时候需要控制某些组件是否展示,一种方法是将这个组件从render tree中删除,这样这个组件就相当
- 工作原理:Spring Cloud框架下的服务发现Eureka包含两个组件分别是: Eureka Server与Eureka ClientE
- RPC是远程过程调用的简称,广泛应用在大规模分布式应用中,作用是有助于系统的垂直拆分,使系统更易拓展。Java中的RPC框架比较多,各有特色
- 近来复习数据结构,自己动手实现了栈。栈是一种限制插入和删除只能在一个位置上的表。最基本的操作是进栈和出栈,因此,又被叫作“先进后出”表。首先
- 在项目中用到了MapStruct,对其可以转换JavaBean特别好奇,因为之前都是使用Vo的方式手动set转换,但是接触到MapStruc
- 有时候需要根据条件查询得出的数据较多,需要分页显示到页面上。这时点击下一页就不方便每次带查询条件在数据库中分页。可以在list中进行分页。p
- 写在前面在Java8之前的版本中,接口中只能声明常量和抽象方法,接口的实现类中必须实现接口中所有的抽象方法。而在Java8中,接口中可以声明
- 文件上传在web应用中非常普遍,要在jsp环境中实现文件上传功能是非常容易的,因为网上有许多用java开发的文件上传组件,本文以common
- 本文实例为大家分享了Android绝对布局AbsoluteLayout的具体代码,供大家参考,具体内容如下1>AbsoluteLayo
- 在微信公众号里面需要上传头像,时间比较紧,调用学习jssdk并使用 来不及 就用了input1、使用input:file标签, 去调用系统默
- 本文实例为大家分享了Java实现马踏棋盘的具体代码,供大家参考,具体内容如下马在某个点最多可能有8种走法,用递归和回溯实现。注:代码中,查找
- 本文实例为大家分享了Java实现24点小游戏的具体代码,供大家参考,具体内容如下程序设计要求:24点游戏是经典的纸牌益智游戏。常见游戏规则:
- 关于springmvc上传图片的方法小编给大家整理了两种方法,具体内容如下所示:第一种:(放在该项目下的物理地址对应的位置)a. 路径写法:
- 1. Mybatis的@param注解自定义对象也用@param注解注:使用@param注解,mapper.xml 不加parameterT
- 我就废话不多说啦,大家还是直接看代码吧~[ { "orderNo": "3213123123123
- 本文实例介绍了基于socket和javaFX简单文件传输工具,分享给大家供大家参考,具体内容如下package application;im