SpringBoot 中發布ApplicationEventPublisher,監聽ApplicationEvent 異步操作


有這么一個業務場景:當用戶注冊后,發送郵件到其郵箱提示用戶進行賬號激活,且注冊成功的同時需要贈送新人用戶體驗卡券。

在這里插入圖片描述

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

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了。

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

示例代碼可以點擊此處下載:戳我戳我


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM