如何在 Spring 中使用事件

【注】本文譯自:?https://www.baeldung.com/spring-events

1.?概述
在本教程中,我們將討論如何在 Spring 中使用事件。
.事件是框架中最容易被忽視的功能之一,但也是更有用的功能之一。和 Spring 中的許多其他東西一樣,事件發(fā)布是 applicationContext 提供的功能之一。
有一些簡單的指導(dǎo)方針可以遵循:
如果我們使用 Spring Framework 4.2 之前的版本,事件類應(yīng)該擴展 applicationEvent。從 4.2 版本開始,事件類不再需要擴展 applicationEvent 類。
發(fā)布者應(yīng)該注入一個 applicationEventPublisher 對象。
監(jiān)聽器應(yīng)實現(xiàn) applicationListener 接口。
2. 自定義事件
Spring 允許我們創(chuàng)建和發(fā)布默認同步的自定義事件。這有一些優(yōu)點,例如偵聽器能夠參與發(fā)布者的事務(wù)上下文。
2.1. 一個簡單的應(yīng)用程序事件
讓我們創(chuàng)建一個簡單的事件類——只是一個用于存儲事件數(shù)據(jù)的占位符。
在這種情況下,事件類包含一個 String 消息:
2.2. 發(fā)布者
現(xiàn)在讓我們創(chuàng)建該事件的發(fā)布者。發(fā)布者構(gòu)造事件對象并將其發(fā)布給正在收聽的任何人。
要發(fā)布事件,發(fā)布者可以簡單地注入 applicationEventPublisher 并使用 publishEvent() API:
或者,發(fā)布者類可以實現(xiàn) applicationEventPublisherAware 接口,這也會在應(yīng)用程序啟動時注入事件發(fā)布者。通常,將 @Autowire 注入發(fā)布者會更簡單。
從 Spring Framework 4.2 開始,applicationEventPublisher 接口為 publishEvent(Object event) 方法提供了一個新的重載,該方法接受任何對象作為事件。因此,Spring 事件不再需要擴展 applicationEvent 類。
2.3. 監(jiān)聽器
最后,讓我們創(chuàng)建監(jiān)聽器。
監(jiān)聽器的唯一要求是是一個 bean 并實現(xiàn) applicationListener 接口:
請注意我們的自定義偵聽器如何使用自定義事件的通用類型進行參數(shù)化,這使得 onapplicationEvent() 方法類型安全。這也避免了必須檢查對象是否是特定事件類的實例并對其進行轉(zhuǎn)換。
而且,正如已經(jīng)討論過的(默認情況下,Spring 事件是同步的), doStuffAndPublishAnEvent() 方法會阻塞,直到所有偵聽器完成對事件的處理。
3. 創(chuàng)建異步事件
在某些情況下,同步發(fā)布事件并不是我們真正想要的——我們可能需要異步處理我們的事件。
我們可以通過創(chuàng)建一個帶有執(zhí)行程序的 applicationEventMulticaster bean 在配置中打開它。
對于我們來說 SimpleAsyncTaskExecutor 很好地實現(xiàn)了這個目的:
事件、發(fā)布者和偵聽器實現(xiàn)與以前相同,但現(xiàn)在監(jiān)聽器將在單獨的線程中異步處理事件。
4. 現(xiàn)有框架事件
Spring 本身發(fā)布了各種開箱即用的事件。例如,applicationContext 將觸發(fā)各種框架事件:ContextRefreshedEvent、ContextStartedEvent、RequestHandledEvent 等。
這些事件為應(yīng)用程序開發(fā)人員提供了一個選項,可以連接到應(yīng)用程序的生命周期和上下文,并在需要的地方添加他們自己的自定義邏輯。
這是監(jiān)聽上下文刷新的監(jiān)聽器的快速示例:
要了解有關(guān)現(xiàn)有框架事件的更多信息,請在此處查看我們的下一個教程。
5. 注解驅(qū)動的事件監(jiān)聽器
從 Spring 4.2 開始,事件偵聽器不需要是實現(xiàn) applicationListener 接口的 bean——它可以通過 @EventListener 注解在托管 bean 的任何 public 方法上注冊:
和以前一樣,方法簽名聲明了它使用的事件類型。
默認情況下,偵聽器是同步調(diào)用的。但是,我們可以通過添加 @Async 注釋輕松地使其異步。我們只需要記住在應(yīng)用程序中啟用異步支持。
6. 泛型支持
也可以使用事件類型中的泛型信息來調(diào)度事件。
6.1. 泛型應(yīng)用程序事件
讓我們創(chuàng)建一個泛型事件類型。
在我們的示例中,事件類包含任何內(nèi)容和?success 狀態(tài)指示器:
請注意 GenericSpringEvent 和 CustomSpringEvent 之間的區(qū)別。我們現(xiàn)在可以靈活地發(fā)布任意事件,并且不再需要從 applicationEvent 擴展。
6.2. 監(jiān)聽器
現(xiàn)在讓我們創(chuàng)建該事件的偵聽器。
我們可以像以前一樣通過實現(xiàn) applicationListener 接口來定義監(jiān)聽器:
但不幸的是,這個定義要求我們從 applicationEvent 類繼承 GenericSpringEvent。因此,對于本教程,讓我們使用之前討論過的注釋驅(qū)動事件偵聽器。
通過在 @EventListener 注釋上定義布爾 SpEL 表達式,也可以使事件偵聽器有條件。
在這種情況下,只有成功調(diào)用?GenericSpringEvent?的 String?對象時才會調(diào)用事件處理程序:
Spring 表達式語言 (SpEL) 是一種強大的表達式語言,在另一篇教程中有詳細介紹。
6.3. 發(fā)布者
事件發(fā)布者與上述類似。但是由于類型擦除,我們需要發(fā)布一個事件來解析我們將過濾的泛型參數(shù),例如,類 GenericStringSpringEvent extends GenericSpringEvent<String>。
此外,還有一種發(fā)布事件的替代方法。如果我們從使用 @EventListener 注解的方法返回一個非空值作為結(jié)果,Spring Framework 會將該結(jié)果作為新事件發(fā)送給我們。此外,通過將多個新事件作為事件處理的結(jié)果返回到一個集合中,我們可以發(fā)布多個新事件。
7. 事務(wù)綁定事件
本節(jié)是關(guān)于使用 @TransactionalEventListener 注解的。要了解有關(guān)事務(wù)管理的更多信息,請查看?使用? Spring 和 JPA 事務(wù)。
從 Spring 4.2 開始,框架提供了一個新的 @TransactionalEventListener 注解,它是 @EventListener 的擴展,它允許將事件的偵聽器綁定到事務(wù)的某個階段。
可以綁定到以下事務(wù)階段:
AFTER_COMMIT(默認)- 用于在事務(wù)成功完成時觸發(fā)事件。
AFTER_ROLLBACK – 如果事務(wù)已回滾
AFTER_COMPLETION – 如果事務(wù)已完成(AFTER_COMMIT 和 AFTER_ROLLBACK 的別名)
BEFORE_COMMIT - 用于在事務(wù)提交之前觸發(fā)事件。
這是一個事務(wù)性事件偵聽器的快速示例:
復(fù)制代碼
僅當(dāng)存在事件生產(chǎn)者正在運行且即將提交的事務(wù)時,才會調(diào)用此監(jiān)聽器。
如果沒有事務(wù)在運行,則根本不會發(fā)送事件,除非我們通過將 fallbackExecution 屬性設(shè)置為 true 來覆蓋它。
8. 結(jié)論
在這篇簡短的文章中,我們介紹了在 Spring 中處理事件的基礎(chǔ)知識,包括創(chuàng)建一個簡單的自定義事件、發(fā)布它,然后在偵聽器中處理它。
我們還簡要了解了如何在配置中啟用事件的異步處理。
然后我們了解了 Spring 4.2 中引入的改進,例如注解驅(qū)動的監(jiān)聽器、更好的泛型支持和事件綁定到事務(wù)階段。
與往常一樣,本文中提供的代碼可在?GitHub 上(https://github.com/eugenp/tutorials/tree/master/spring-core-2)獲得。這是一個基于 Maven 的項目,因此它應(yīng)該很容易導(dǎo)入和運行。