SpringBoot 中釋出ApplicationEventPublisher,監聽ApplicationEvent 非同步操作

wshanshi發表於2021-11-09
有這麼一個業務場景:當使用者註冊後,傳送郵件到其郵箱提示使用者進行賬號啟用,且註冊成功的同時需要贈送新人使用者體驗卡券。

在這裡插入圖片描述

業務有了,那麼問題也就來了。

What? 問題....問題?我聽說你有問題? 來拔刀吧,互相傷害啊。
在這裡插入圖片描述
考慮以下兩個問題:如何註冊成功立即傳送郵件、贈送體驗卡? 如何同時向使用者郵箱傳送啟用郵件、贈送新人客戶體驗卡,互不影響?
在這裡插入圖片描述
如果是微服務專案,該邏輯可結合訊息中介軟體進行處理。若是單機程式碼,有什麼好的辦法哇?你還在瘋狂的程式碼邏輯判斷嗎?若程式碼高耦合,後期進行維護彷彿並不是那麼happy,就不用說在此基礎上擴充套件業務了。

So 請了解下Spring中事件機制:釋出ApplicationEventPublisher,實現監聽ApplicationEvent。結合非同步操作,哎呀,真香!你值得擁有!
在這裡插入圖片描述
下面就跟著樓主的小碎步,慢慢帶你帶入坑。“氣死我了,上才藝。EG埃meng,EG埃meng,EG埃meng。你說我是.....”

說歸說,鬧歸鬧,不拿程式碼開玩笑。迴歸正題,直接上程式碼。哇哈哈哈哈哈哈......

一、 首先定義下使用者類:

樓主示例這個使用者類屬性寫的比較隨意,只做測試看效果哈。
在這裡插入圖片描述

二、定義一個Event事件類:

注意:自定義事件類繼承ApplicationEvent類,重寫方法
在這裡插入圖片描述
該類中屬性根據業務需求自定義即可。

如下所示,樓主定義的Event類叫做UserActionEvent。
在這裡插入圖片描述
EnumUserOperate 列舉類
在這裡插入圖片描述

三、事件類定義好了,我們去定義操作釋出:ApplicationEventPublisher,快點跟上別掉隊了。

在這裡插入圖片描述

我是在UserServiceImpl中進行事件釋出的,如下:
在這裡插入圖片描述
釋出者會呼叫 ApplicationEventPublisher的publishEvent 方法對某一事件進行釋出。隨後Spring容器會把該事件告訴所有的監聽者(我的“女神”有動態了),監聽者根據拿到的“資訊、某些指令或者某些資料”去做一些業務上的操作。

這個模式常常會與設計模式中觀察者模式進行對比。舉個例子:上課鈴響了,老師和同學聽到鈴聲後,都來班裡了(老師要上課,學生要聽課)。在這個事件裡,被觀察的是“鈴聲”,“鈴聲響了”是一種狀態,或者說是一種通知。告訴大家:該上課了。

四、釋出事件後該定義監聽了:

自定義監聽方法上方新增註解:@EventListener()。

眼尖的小夥伴會發現,樓主這裡使用表示式condition = "#event.operate.name()=='ADD'"對監聽進行了細化:監聽型別為“新增”的事件

注意:自定義監聽必須交給spring容器管理,否則不起作用哈。如下圖加@Component註解就行(兄弟,交保護費了。額....不交也行,但是必須得跟著spring混....)

@Async()會在下面說
在這裡插入圖片描述
釋出和監聽都設定好了,使用快樂的postman傳送下請求......
在這裡插入圖片描述
測試結果如下:
在這裡插入圖片描述
加了表示式的只會監聽到指定型別的事件。當然這裡你可以加別的條件,根據業務怎麼開心怎麼來嘛,對不?

在這裡插入圖片描述

釋出和監聽可以了,那我併發操作的時候如何保證不會阻塞,互不影響呢?

非同步啊,在加個執行緒池。

問一句:“老哥,為啥加執行緒池?”

多執行緒操作,反覆建立銷燬,效能消耗是很大的。使用執行緒池降低資源消耗,提高利用率,加上非同步操作速度還快,何樂而不為呢。

五、方法非同步:

定義方法上方加@Async()註解就好了。

非同步方法可以指定使用某一執行緒池:如 @Async("lazyTraceExecutor"),lazyTraceExecutor是執行緒池Bean物件的名字

六、執行緒池自定義:

不知道有沒有人diss樓主只截圖,不貼程式碼。這不,他來了他來了....

@Configuration
public class Configurer implements AsyncConfigurer {

//    @Autowired
//    private BeanFactory beanFactory;

    /**
     * 自定義執行緒池
     *
     * @return
     */
    @Bean("lazyTraceExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //此方法返回可用處理器的虛擬機器的最大數量; 不小於1
        int core = Runtime.getRuntime().availableProcessors();
        executor.setCorePoolSize(core);//設定核心執行緒數
        executor.setMaxPoolSize(core * 2 + 1);//設定最大執行緒數
        executor.setKeepAliveSeconds(3);//除核心執行緒外的執行緒存活時間
        executor.setQueueCapacity(40);//如果傳入值大於0,底層佇列使用的是LinkedBlockingQueue,否則預設使用SynchronousQueue
        executor.setThreadNamePrefix("my-executor-");//執行緒名稱字首
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//設定拒絕策略
//        return new LazyTraceThreadPoolTaskExecutor(beanFactory, executor);
        executor.initialize();

        return executor;
    }
}

最後,別忘了在啟動項上加@EnableAsync註解哦!

想了解ApplicationEventPublisher和ApplicationEvent原理的,認準spring官網happy哈。這裡就不過多介紹了。

傲嬌的wshanshi要go to sleep了。

在這裡插入圖片描述
靚女,帥仔。你有沒有那個,那個小心心.... 沒有!呸,渣男。啥也不是,散會!

示例程式碼可以點選此處下載:戳我戳我

相關文章