SpringBoot2.X 實(shí)戰(zhàn)5 - Event

一.簡(jiǎn)介

當(dāng)一個(gè)事件發(fā)生時(shí),我們希望另外一個(gè)服務(wù)能夠感知到這個(gè)事件.發(fā)布這個(gè)事件的對(duì)象不需要關(guān)心哪個(gè)對(duì)象會(huì)監(jiān)聽(tīng)這個(gè)事件并去處理它,當(dāng)然監(jiān)聽(tīng)者也不需要知道發(fā)布這個(gè)事件的對(duì)象是誰(shuí),我只關(guān)心如何去處理這些事件.這樣業(yè)務(wù)之間的邏輯會(huì)更加解耦提高可擴(kuò)展性.

從Spring框架的一開(kāi)始蔑歌,應(yīng)用程序事件(ApplicationEvent)就成為松散耦合組件交換信息的一種手段,其實(shí)現(xiàn)原理為觀察者模式.

二.基本用法

1.定義事件
所有的事件均需繼承自 ApplicationEvent.ApplicationEvent是個(gè)抽象類(lèi),這樣我們可以在實(shí)現(xiàn)類(lèi)中定義一些用到的數(shù)據(jù).繼承 ApplicationEvent 后必須要重載構(gòu)造方法,參數(shù)可以自定義.第一個(gè) Object source 表示事件的發(fā)布體,發(fā)布事件時(shí)可以用 this.String data 表示自定義的數(shù)據(jù),這里也可以用其他復(fù)雜對(duì)象來(lái)代替.

public class NormalEvent extends ApplicationEvent {

    @Getter
    private String data;

    public NormalEvent(Object source, String data) {
        super(source);
        this.data = data;
    }

}

2.發(fā)布事件
發(fā)布事件需要我們拿到 ApplicationContext 對(duì)象,在這個(gè)對(duì)象中調(diào)用publishEvent 方法.拿到 ApplicationContext 對(duì)象方法由很多.
這里先使用最簡(jiǎn)單的構(gòu)造方法注入方式.
注意:發(fā)布事件時(shí),第一個(gè)參數(shù)為 this.


@RestController
public class EventController1 {

    private final ApplicationContext applicationContext;

    public EventController1(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @GetMapping("/get1")
    public String getString1() {
        applicationContext.publishEvent(new NormalEvent(this, "NormalEvent"));
        return "get1";
    }
}

3.事件監(jiān)聽(tīng)器
注冊(cè)事件監(jiān)聽(tīng)器方式也很多,但是用 @EventListener 方式是最簡(jiǎn)單的,在任何方法public void 方法中加上注解都可以. @EventListener 將方法標(biāo)記為應(yīng)用程序事件的偵聽(tīng)器的注釋姜性。
方法的入口參數(shù)即為事件,當(dāng)入口參數(shù)為 Object 時(shí),任何事件均會(huì)監(jiān)聽(tīng)到.


@Slf4j
@Component
public class NormalEventHandler {

    /**
     * @param event 任何類(lèi)型的事件都會(huì)接受
     */
    @EventListener
    public void event(Object event) {
        log.error("NormalEventHandler(Object) 接收到事件:{}", event.getClass());
    }

    /**
     * @param event 會(huì)接受 NormalEvent 類(lèi)型的事件,順序不定
     */
    @EventListener
    public void event1(NormalEvent event) {
        log.error("NormalEventHandler_event1(NormalEvent) 接收到事件:{}", event.getClass());
    }

    /**
     * @param event 會(huì)接受 會(huì)接受 NormalEvent 類(lèi)型的事件,順序不定
     */
    @EventListener
    public void event2(NormalEvent event) {
        log.error("NormalEventHandler_event2(NormalEvent) 接收到事件:{}", event.getClass());
    }

}

三.SmartEvent

二中介紹的時(shí)間監(jiān)聽(tīng)器之間都是毫無(wú)關(guān)系的.經(jīng)常發(fā)布的事件需要被多個(gè)監(jiān)聽(tīng)器監(jiān)聽(tīng)到,并且需要一定的順序去執(zhí)行這些監(jiān)聽(tīng)器.
1.定義事件
這個(gè)和普通的事件沒(méi)有任何區(qū)別.


public class SmartEvent extends ApplicationEvent {

    @Getter
    private String data;

    public SmartEvent(Object source, String data) {
        super(source);
        this.data = data;
    }

}

2.發(fā)布事件
這個(gè)也和普通的事件發(fā)布沒(méi)有任何區(qū)別

@RestController
public class EventController1 {

    private final ApplicationContext applicationContext;

    public EventController1(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @GetMapping("/get3")
    public String getString3() {
        applicationContext.publishEvent(new SmartEvent(this, "SmartEvent"));
        return "get3";
    }

}

3.Smart事件監(jiān)聽(tīng)器

這時(shí)時(shí)間監(jiān)聽(tīng)器需要 實(shí)現(xiàn) SmartApplicationListener 接口.SmartApplicationListener 是標(biāo)準(zhǔn)的 ApplicationListener 以及Ordered 接口的擴(kuò)展變體.暴露更多的元數(shù)據(jù),如支持的時(shí)間以及源類(lèi)型.
需要實(shí)現(xiàn) supportsEventType , supportsSourceType , getOrder 三個(gè)方法.

  • supportsEventType:表示支持的事件類(lèi)型,,可以為多個(gè),只要該方法返回 true 即可.
  • supportsSourceType:表示支持的源類(lèi)型,即是從哪個(gè)類(lèi)中發(fā)布的這個(gè)事件.
  • getOrder:監(jiān)聽(tīng)器執(zhí)行的優(yōu)先級(jí).數(shù)字越大優(yōu)先級(jí)越低.
@Slf4j
@Component
public class SmartEventHandler1 implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        // 只接收 SmartEvent 類(lèi)型的事件,只有 SmartEvent 類(lèi)型的事件才會(huì)執(zhí)行下面的邏輯
        return aClass == SmartEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        // //只有在 EventController1 內(nèi)發(fā)布的 SmartEvent 事件時(shí)才會(huì)執(zhí)行下面邏輯
        return aClass == EventController1.class;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        SmartEvent event = (SmartEvent) applicationEvent;
        log.error("SmartEventHandler1:{}", event.getData());
    }

    @Override
    public int getOrder() {
        return 1;
    }

}

四.事件反饋

經(jīng)常給系統(tǒng)一個(gè)輸入就希望系統(tǒng)能夠給一個(gè)反饋,如接口調(diào)用.因?yàn)榘l(fā)布者不關(guān)系監(jiān)聽(tīng)者,監(jiān)聽(tīng)者也不關(guān)心發(fā)布者,所以正常情況下沒(méi)有辦法給接口一個(gè)返回,這時(shí)只能使用統(tǒng)一異常處理.

1.異常事件

public class NormalExceptionEvent extends ApplicationEvent {

    @Getter
    private String data;

    public NormalExceptionEvent(Object source, String data) {
        super(source);
        this.data = data;
    }

}

2.發(fā)布異常事件

@RestController
public class EventController1 {

    private final ApplicationContext applicationContext;

    public EventController1(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @GetMapping("/get2")
    public String getString2() {
        applicationContext.publishEvent(new NormalExceptionEvent(this, "NormalExceptionEvent"));
        return "get2";
    }

}

3.定義異常類(lèi)

public class EventException extends RuntimeException {

    @Getter
    @Setter
    private String mess;

    public EventException(String mess) {
        this.mess = mess;
    }
    
}

4.定義異常處理類(lèi)


@RestControllerAdvice
public class EventExceptionHandler {

    @ExceptionHandler(value = EventException.class)
    public String handler1(EventException exception) {
        return "error:" + exception.getMess();
    }

}

5.拋出異常


@Slf4j
@Component
public class NormalExceptionEventHandler {

    /**
     * @param event 會(huì)接受 NormalEvent 類(lèi)型的事件,順序不定
     */
    @EventListener
    public void event1(NormalExceptionEvent event) {
        log.error("NormalEventHandler_event1(NormalEvent) 接收到事件:{}", event.getClass());
        throw new EventException("NormalExceptionEvent!");
    }

}

注:經(jīng)測(cè)試,普通的事件和 SmartEvent 在 SpringBoot2.0.6 中均支持統(tǒng)一異常處理.但是之前好像有遇到有些 SpringBoot 版本不支持的情況.

五.代碼路徑

https://github.com/shaopro/SpringBootEvent

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瘸恼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沮稚,老刑警劉巖辨绊,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悬钳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡棘捣,警方通過(guò)查閱死者的電腦和手機(jī)辜腺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乍恐,“玉大人评疗,你說(shuō)我怎么就攤上這事∫鹆遥” “怎么了百匆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)呜投。 經(jīng)常有香客問(wèn)我加匈,道長(zhǎng),這世上最難降的妖魔是什么仑荐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任雕拼,我火速辦了婚禮,結(jié)果婚禮上粘招,老公的妹妹穿的比我還像新娘啥寇。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布辑甜。 她就那樣靜靜地躺著衰絮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪磷醋。 梳的紋絲不亂的頭發(fā)上猫牡,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音子檀,去河邊找鬼镊掖。 笑死乃戈,一個(gè)胖子當(dāng)著我的面吹牛褂痰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播症虑,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缩歪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了谍憔?” 一聲冷哼從身側(cè)響起匪蝙,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎习贫,沒(méi)想到半個(gè)月后逛球,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苫昌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年颤绕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祟身。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奥务,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出袜硫,到底是詐尸還是另有隱情氯葬,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布婉陷,位于F島的核電站帚称,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏秽澳。R本人自食惡果不足惜世杀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肝集。 院中可真熱鬧瞻坝,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至浮创,卻和暖如春忧吟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背斩披。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工溜族, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垦沉。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓煌抒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親厕倍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寡壮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容