Spring 中標(biāo)準(zhǔn)事件與自定義事件

簡介
ApplicationContext是通過ApplicationEvent 類和ApplicationListener接口提供的。如果將實現(xiàn)ApplicationListener接口的 bean 部署到上下文中,則每次 ApplicationEvent將 發(fā)布到 時ApplicationContext,都會通知該 bean。本質(zhì)上,這是標(biāo)準(zhǔn)的觀察者設(shè)計模式。、
從 Spring 4.2 開始,事件基礎(chǔ)結(jié)構(gòu)得到了顯著改進,并提供了基于注釋的模型以及發(fā)布任意事件(即不一定從 擴展的對象ApplicationEvent)的能力。當(dāng)此類對象發(fā)布時,我們會為您將其包裝在一個事件中。
下表描述了 Spring 提供的標(biāo)準(zhǔn)事件:

您還可以創(chuàng)建和發(fā)布自己的自定義事件。以下示例顯示了一個擴展 SpringApplicationEvent基類的簡單類:
public class BlockedListEvent extends ApplicationEvent {private final String address;private final String content;public BlockedListEvent(Object source, String address, String content) {super(source);this.address = address;this.content = content;}// accessor and other methods...}
要發(fā)布自定義ApplicationEvent,調(diào)用publishEvent()上的方法 ApplicationEventPublisher。通常,這是通過創(chuàng)建一個實現(xiàn)ApplicationEventPublisherAware并將其注冊為 Spring bean的類來完成的 。以下示例顯示了這樣一個類:
public class EmailService implements ApplicationEventPublisherAware {private List<String> blockedList;private ApplicationEventPublisher publisher;public void setBlockedList(List<String> blockedList) {this.blockedList = blockedList;}public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}public void sendEmail(String address, String content) {if (blockedList.contains(address)) {publisher.publishEvent(new BlockedListEvent(this, address, content));return;}// send email...}}
在配置時,Spring 容器會檢測到EmailService實現(xiàn) ApplicationEventPublisherAware并自動調(diào)用 setApplicationEventPublisher(). 實際上,傳入的參數(shù)是Spring容器本身。您正在通過其ApplicationEventPublisher界面與應(yīng)用程序上下文進行 交互。
要接收 custom ApplicationEvent,您可以創(chuàng)建一個實現(xiàn) ApplicationListener并將其注冊為 Spring bean 的類。以下示例顯示了這樣一個類:
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {private String notificationAddress;public void setNotificationAddress(String notificationAddress) {this.notificationAddress = notificationAddress;}public void onApplicationEvent(BlockedListEvent event) {// notify appropriate parties via notificationAddress...}}
請注意,ApplicationListener它通常使用自定義事件的類型進行參數(shù)化(BlockedListEvent在前面的示例中)。這意味著該 onApplicationEvent()方法可以保持類型安全,避免任何向下轉(zhuǎn)型的需要。您可以根據(jù)需要注冊任意數(shù)量的事件偵聽器,但請注意,默認(rèn)情況下,事件偵聽器會同步接收事件。這意味著該publishEvent()方法會阻塞,直到所有偵聽器都完成對事件的處理。這種同步和單線程方法的一個優(yōu)點是,當(dāng)偵聽器接收到事件時,如果事務(wù)上下文可用,它就會在發(fā)布者的事務(wù)上下文中運行。
總而言之,當(dāng)調(diào)用 bean的sendEmail()方法時emailService,如果有任何電子郵件消息應(yīng)該被阻止,BlockedListEvent則會發(fā)布一個自定義事件類型 。該blockedListNotifierbean被注冊為 ApplicationListener接收的BlockedListEvent,在這一點上,可以通知有關(guān)各方。
Spring 的事件機制是為同一應(yīng)用程序上下文中 Spring bean 之間的簡單通信而設(shè)計的。然而,對于更復(fù)雜的企業(yè)集成需求,單獨維護的 Spring Integration項目為構(gòu)建基于眾所周知的 Spring 編程模型的輕量級、面向模式、事件驅(qū)動的架構(gòu)提供了完整的支持 。
基于注解的事件監(jiān)聽器
您可以使用@EventListener注釋在托管 bean 的任何方法上注冊事件偵聽器 。該BlockedListNotifier可改寫如下:
public class BlockedListNotifier {private String notificationAddress;public void setNotificationAddress(String notificationAddress) {this.notificationAddress = notificationAddress;}@EventListenerpublic void processBlockedListEvent(BlockedListEvent event) {// notify appropriate parties via notificationAddress...}}
方法簽名再次聲明了它偵聽的事件類型,但這次使用了靈活的名稱并且沒有實現(xiàn)特定的偵聽器接口。只要實際事件類型在其實現(xiàn)層次結(jié)構(gòu)中解析您的泛型參數(shù),也可以通過泛型縮小事件類型。
如果您的方法應(yīng)該偵聽多個事件,或者您想定義它時根本不帶參數(shù),則還可以在注釋本身上指定事件類型。以下示例顯示了如何執(zhí)行此操作:
({ContextStartedEvent.class, ContextRefreshedEvent.class})public void handleContextStart() {// ...}
還可以通過使用condition定義SpEL表達式的注釋的屬性添加額外的運行時過濾,該屬性應(yīng)該匹配以實際調(diào)用特定事件的方法。
以下示例顯示了如何重寫我們的通知程序,使其僅content在事件的屬性等于時才被調(diào)用 my-event:
(condition = "#blEvent.content == 'my-event'")public void processBlockedListEvent(BlockedListEvent blEvent) {// notify appropriate parties via notificationAddress...}
如果您需要發(fā)布一個事件作為處理另一個事件的結(jié)果,您可以更改方法簽名以返回應(yīng)該發(fā)布的事件,如以下示例所示:
@EventListenerpublic ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {// notify appropriate parties via notificationAddress and// then publish a ListUpdateEvent...}
項目實戰(zhàn)代碼地址https://github.com/yanghaiji/IT-Demo
