有這麼一個業務場景:當使用者註冊後,傳送郵件到其郵箱提示使用者進行賬號啟用,且註冊成功的同時需要贈送新人使用者體驗卡券。
業務有了,那麼問題也就來了。
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了。
靚女,帥仔。你有沒有那個,那個小心心.... 沒有!呸,渣男。啥也不是,散會!
示例程式碼可以點選此處下載:戳我戳我