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

- 1>定義事件,并綁定到事件源中
- 2>定義事件監(jiān)聽(tīng)器,監(jiān)聽(tīng)事件源
- 3>用戶某行為觸發(fā)事件
- 4>事件監(jiān)聽(tīng)器監(jiān)控到事件發(fā)送,執(zhí)行事件響應(yīng)邏輯。
以上面的登錄為例:
- 事件源:login方法
- 事件:用戶login行為
- 事件監(jiān)聽(tīng)器:此處沒(méi)有,需要額外定制,但是事件響應(yīng):送積分,發(fā)郵件,發(fā)短信。
事件實(shí)現(xiàn)
以登錄為例子實(shí)現(xiàn)事件監(jiān)聽(tīng)機(jī)制
1>定義抽象事件
/**
* 抽象事件類(lèi)
* 作用:定制事件邏輯
*/
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)聽(tīng)器
/**
* 事件監(jiān)聽(tīng)器
* 作用:當(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)聽(tīng)器
積分監(jiān)聽(tīng)器
/**
* 加積分監(jiān)聽(tīng)器器:
* 當(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)聽(tīng)器
/**
* 加積分監(jiān)聽(tīng)器器:
* 當(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)聽(tīng)器
/**
* 加積分監(jiān)聽(tīng)器器:
* 當(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>注冊(cè)事件監(jiān)聽(tīng)器
* 2>刪除事件監(jiān)聽(tīng)器
* 3>事件觸發(fā)時(shí),廣播事件
*/
public interface EventMulticaster {
//廣播事件
void multicastEvent(AbstractEvent event);
//注冊(cè)事件監(jiān)聽(tīng)器
void registListener(EventListener listener);
//刪除事件監(jiān)聽(tīng)器
void removeListener(EventListener listener);
}6>定制簡(jiǎn)單的事件廣播器
/**
* 事件廣播器實(shí)現(xiàn)類(lèi)
* 作用:維護(hù)事件監(jiān)聽(tīng)器
*/
public class SimpleEventMulticaster implements EventMulticaster {
//key:事件字節(jié)碼對(duì)象, value:當(dāng)前事件綁定的事件監(jiān)聽(tīng)器
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)聽(tīng)器綁定的事件
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)聽(tīng)器綁定的事件
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>綜合測(cè)試
public class App {
//1:初始化事件廣播器
public static SimpleEventMulticaster multicaster = new SimpleEventMulticaster();
static {
//2:注冊(cè)監(jiān)聽(tīng)器
//登陸事件上綁定3個(gè)監(jiān)聽(tīng)器
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)聽(tīng)詳情解析的文章就介紹到這了,更多相關(guān)Java 事件監(jiān)聽(tīng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Eclipse+Java+Swing+Mysql實(shí)現(xiàn)工資管理系統(tǒng)
這篇文章主要介紹了Eclipse+Java+Swing+Mysql實(shí)現(xiàn)工資管理系統(tǒng),對(duì)正在工作或者學(xué)習(xí)的你有一定的參考價(jià)值,需要的朋友可以參考一下2022-01-01
SpringBoot選擇自有bean優(yōu)先加載實(shí)現(xiàn)方法
在一些需求中,可能存在某些場(chǎng)景,比如先加載自己的bean,然后自己的bean做一些DB操作,初始化配置問(wèn)題,然后后面的bean基于這個(gè)配置文件,繼續(xù)做其他的業(yè)務(wù)邏輯。因此有了本文的這個(gè)題目2023-03-03
Gradle環(huán)境下導(dǎo)出Swagger為PDF的步驟詳解
這篇文章主要介紹了Gradle環(huán)境下導(dǎo)出Swagger為PDF的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
簡(jiǎn)單了解JAVA public class與class區(qū)別
這篇文章主要介紹了簡(jiǎn)單了解JAVA public class與class區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
JVM的內(nèi)存回收及常見(jiàn)算法小結(jié)
這篇文章主要介紹了JVM的內(nèi)存回收及常見(jiàn)算法,需要的朋友可以參考下2023-05-05
使用IDEA創(chuàng)建SpringBoot項(xiàng)目的方法步驟
這篇文章主要介紹了使用IDEA創(chuàng)建SpringBoot項(xiàng)目的方法步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
SpringCloud超詳細(xì)講解微服務(wù)網(wǎng)關(guān)Zuul
這篇文章主要介紹了SpringCloud Zuul微服務(wù)網(wǎng)關(guān),負(fù)載均衡,熔斷和限流,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07

