Java?Spring?事件監(jiān)聽詳情解析
前言
前段時(shí)間因?yàn)楣ぷ鞯男枰玫絊pring事件,翻翻文檔將功能實(shí)現(xiàn)了,但是存在少許理解不暢的地方,今天有空來梳理梳理。
需求背景
葉子同學(xué)在新入職公司,老大讓他實(shí)現(xiàn)登陸功能,葉子隨手寫完,上線無bug,一切安好
//登陸偽代碼
public void login(....){
userLogin(....);
}幾天之后,老大說為維護(hù)用戶的粘度,每天登陸送積分。葉子同學(xué),二話不說,一頓操作后,上線無bug,一切安好
//登陸偽代碼
public void login(....){
//登陸
userLogin(....);
//送積分
loginPoint(....)
}又幾天后,老大說,為了客戶安全,每次異地登陸發(fā)送郵件。葉子同學(xué)稍微抱怨,看在錢份上又是一頓操作后,上線無bug, 一切安好
//登陸偽代碼
public void login(....){
//登陸
userLogin(....);
//送積分
loginPoint(....)
//發(fā)送郵件
sendEmail(....)
}又又幾天后,老大說,部分客戶不用郵件,用短信。葉子同學(xué)壓著怒氣,看著銀行卡,又是一頓操作后,上線無bug, 一切安好
//登陸偽代碼
public void login(....){
//登陸
userLogin(....);
//送積分
loginPoint(....)
//發(fā)送郵件
sendEmail(....)
//發(fā)短信
sendSms(...)
}又又又幾天后,老大還沒開口,葉子同學(xué)就忍無可忍啦,得加錢。老大哄了好久,說不改需求了。改bug,用戶抱怨登陸慢,有時(shí)還不成功。葉子二話不說,接手排查,查出問題啦
- 1> 郵件發(fā)送耗時(shí)
- 2>同步實(shí)現(xiàn),如果郵件發(fā)送超時(shí),登陸會出異常
代碼改進(jìn):
//登陸偽代碼
public void login(....){
//登陸
userLogin(....);
try{
//送積分
new Thread(()->loginPoint(....)).start();
//發(fā)送郵件
new Thread(()->sendEmail(....)).start();
//發(fā)短信
new Thread(()->sendSms(....)).start();
}catch(Exception e){
//異常處理
}
}問題解決,功能實(shí)現(xiàn),ok~
又又又又幾天之后,老大說,只需要實(shí)現(xiàn)登陸功能即可,其他都不要~
葉子同學(xué)一句臥槽,然后是一段含母非常高的國粹。
此時(shí),問:如果你是葉子同學(xué),你有啥方案能優(yōu)雅應(yīng)對上面的需求變更呢?
一種優(yōu)雅方案:事件監(jiān)聽機(jī)制
事件概念
定義
事件監(jiān)聽機(jī)制:就是對一個(gè)事件(行為動(dòng)作)進(jìn)行監(jiān)聽,當(dāng)外界觸發(fā)某事件時(shí),監(jiān)聽程序馬上被捕獲該事件,并觸發(fā)相應(yīng)的響應(yīng),這過程稱之為事件監(jiān)聽機(jī)制。
組成
事件監(jiān)聽機(jī)制有3個(gè)核心組成部分:
- 1>事件,標(biāo)記某種行為動(dòng)作,比如:鼠標(biāo)點(diǎn)擊事件,鼠標(biāo)移動(dòng)事件等。
- 2>事件源,被監(jiān)控的對象或組件,事件發(fā)生的地方。比如:點(diǎn)擊按鈕,觸發(fā)點(diǎn)擊事件,按鈕就是實(shí)現(xiàn)源。
- 3>事件監(jiān)聽器,監(jiān)聽事件的操作類,一旦事件發(fā)生(被觸發(fā)),則執(zhí)行事件監(jiān)聽器預(yù)設(shè)的邏輯,進(jìn)行事件響應(yīng)。

- 1>定義事件,并綁定到事件源中
- 2>定義事件監(jiān)聽器,監(jiān)聽事件源
- 3>用戶某行為觸發(fā)事件
- 4>事件監(jiān)聽器監(jiān)控到事件發(fā)送,執(zhí)行事件響應(yīng)邏輯。
以上面的登錄為例:
- 事件源:login方法
- 事件:用戶login行為
- 事件監(jiān)聽器:此處沒有,需要額外定制,但是事件響應(yīng):送積分,發(fā)郵件,發(fā)短信。
事件實(shí)現(xiàn)
以登錄為例子實(shí)現(xiàn)事件監(jiān)聽機(jī)制
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>定義事件監(jiān)聽器
/**
* 事件監(jiān)聽器
* 作用:當(dāng)監(jiān)控的事件發(fā)送時(shí),執(zhí)行預(yù)設(shè)的邏輯
*/
public interface EventListener<E extends AbstractEvent> {
/**
* 預(yù)設(shè)邏輯方法
* 事件被觸發(fā),馬上執(zhí)行
*/
void onEvent(E event);
}4>定制登陸事件監(jiān)聽器
積分監(jiān)聽器
/**
* 加積分監(jiān)聽器器:
* 當(dāng)用戶登陸事件觸發(fā)后,馬上執(zhí)行
*/
public class PointsListener implements EventListener<LoginEvent> {
public void onEvent(LoginEvent event) {
System.out.println(event.getSource() + "發(fā)生后,執(zhí)行積分+1操作");
System.out.println(Thread.currentThread().getName());
}
}短信監(jiān)聽器
/**
* 加積分監(jiān)聽器器:
* 當(dāng)用戶登陸事件觸發(fā)后,馬上執(zhí)行
*/
public class SmsListener implements EventListener<LoginEvent> {
public void onEvent(LoginEvent event) {
System.out.println(event.getSource() + "發(fā)生后,執(zhí)行發(fā)送短信操作");
System.out.println(Thread.currentThread().getName());
}
}郵件監(jiān)聽器
/**
* 加積分監(jiān)聽器器:
* 當(dāng)用戶登陸事件觸發(fā)后,馬上執(zhí)行
*/
public class EmailListener implements EventListener<LoginEvent> {
public void onEvent(LoginEvent event) {
try {
//模擬10s延時(shí)
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(event.getSource() + "發(fā)生后,執(zhí)行發(fā)送郵件操作");
System.out.println(Thread.currentThread().getName());
}
}5>定義事件廣播器
/**
* 事件廣播器
* 1>注冊事件監(jiān)聽器
* 2>刪除事件監(jiān)聽器
* 3>事件觸發(fā)時(shí),廣播事件
*/
public interface EventMulticaster {
//廣播事件
void multicastEvent(AbstractEvent event);
//注冊事件監(jiān)聽器
void registListener(EventListener listener);
//刪除事件監(jiān)聽器
void removeListener(EventListener listener);
}6>定制簡單的事件廣播器
/**
* 事件廣播器實(shí)現(xiàn)類
* 作用:維護(hù)事件監(jiān)聽器
*/
public class SimpleEventMulticaster implements EventMulticaster {
//key:事件字節(jié)碼對象, value:當(dāng)前事件綁定的事件監(jiān)聽器
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) {
//獲取監(jiān)聽器綁定的事件
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) {
//獲取監(jiān)聽器綁定的事件
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:注冊監(jiān)聽器
//登陸事件上綁定3個(gè)監(jiān)聽器
multicaster.registListener(new PointsListener());
multicaster.registListener(new EmailListener());
multicaster.registListener(new SmsListener());
}
//3:模擬登陸
public static void login(){
//4:用戶登陸成功觸發(fā)登陸事件
System.out.println("用戶執(zhí)行登陸邏輯");
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());
}
}時(shí)序圖

到此這篇關(guān)于Java Spring 事件監(jiān)聽詳情解析的文章就介紹到這了,更多相關(guān)Java 事件監(jiān)聽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot利用切面注解及反射實(shí)現(xiàn)事件監(jiān)聽功能
- SpringBoot監(jiān)聽事件和處理事件程序示例詳解
- Spring事件監(jiān)聽器之@EventListener原理分析
- springboot+redis過期事件監(jiān)聽實(shí)現(xiàn)過程解析
- SpringMVC事件監(jiān)聽ApplicationListener實(shí)例解析
- SpringBoot Application事件監(jiān)聽的實(shí)現(xiàn)方案
- springboot 事件監(jiān)聽的實(shí)現(xiàn)方法
- Spring 事件監(jiān)聽機(jī)制實(shí)現(xiàn)跨模塊調(diào)用的思路詳解
相關(guān)文章
Spring如何動(dòng)態(tài)自定義logback日志目錄詳解
這篇文章主要給大家介紹了關(guān)于Spring如何動(dòng)態(tài)自定義logback日志目錄的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10
詳述IntelliJ IDEA遠(yuǎn)程調(diào)試Tomcat的方法(圖文)
本篇文章主要介紹了詳述IntelliJ IDEA遠(yuǎn)程調(diào)試Tomcat的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12
spring cloud如何修復(fù)zuul跨域配置異常的問題
最近的開發(fā)過程中,使用spring集成了spring-cloud-zuul,在配置zuul跨域的時(shí)候遇到了問題,下面這篇文章主要給大家介紹了關(guān)于spring cloud如何修復(fù)zuul跨域配置異常的問題,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09
淺談java中異步多線程超時(shí)導(dǎo)致的服務(wù)異常
下面小編就為大家?guī)硪黄獪\談java中異步多線程超時(shí)導(dǎo)致的服務(wù)異常。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06
Java實(shí)現(xiàn)JDBC向數(shù)據(jù)庫批量插入
在Java項(xiàng)目中可能會出現(xiàn)大量向數(shù)據(jù)庫中插入的情況,本文主要介紹了Java實(shí)現(xiàn)JDBC向數(shù)據(jù)庫批量插入,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
SpringBoot靜態(tài)視頻實(shí)時(shí)播放的實(shí)現(xiàn)代碼
這篇文章主要介紹了SpringBoot靜態(tài)視頻實(shí)時(shí)播放的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01

