領域設計:領域事件

本文探討如下問題:

  • 什麼是領域事件
  • 領域事件的用途
  • 何時使用領域事件
  • 基於Spring事件的實現

什麼是領域事件

EDA風格與Reactor模式一文中,我們從觀察者模式聊到了EDA架構風格,然後聊了Reactor架構模式,最後以redis爲例聊了Event-driven programming編程模式。

這些內容都是技術層面的,其共性是通過事件來進行解耦,提高系統的性能、擴展性、伸縮性、可運維性和靈活性。

而領域事件顧名思義也是通過事件來進行解耦,只是它是設計層面的技術。用於解耦領域設計中的組件:

何時使用領域事件?

在《領域驅動設計》的第七章,舉了一個貨運的例子。其中有個HandlingEvent,這就是一個事件對象。當貨物被裝貨、卸貨、密封、存放以及其他活動時就會創建一個HandlingEvent。

領域設計:領域事件

 

在這個例子中,是由人在貨物被處理時,使用系統輸入了一條HandlingEvent記錄下來而已!而如果當發生了一個HandlingEvent後,需要後續的處理(比如發送通知給相關人員),該怎麼辦呢?這就是領域事件所要解決的問題!

在《領域驅動設計》中並沒有提到領域事件,領域事件是其後出現的。

實際上,當出現類似如下關鍵詞彙時,都可以考慮使用領域事件:

  • 當......
  • 如果發生......
  • 當......的時候,請通知我
  • 發生......時

下面通過Spring的事件來實現上例中的具體流程。

Spring事件實例代碼

  • 新建HandlingEvent類,繼承ApplicationEvent,表示這是一個事件
public class HandlingEvent extends ApplicationEvent {
 private String actionName;
 public VisitEvent(Object source,String actionName) {
 super(source);
 this.actionName = actionName;
 }
}
  • 編寫事件發佈類EventPublisher,實現ApplicationEventPublisherAware,用於注入ApplicationEventPublisher,通過publishEvent方法來發布事件
  • Spring默認是同步事件,如果需要異步事件,需要添加EnableAsync註解
@Service@EnableAsyncpublic class EventPublisher implements ApplicationEventPublisherAware {
 private ApplicationEventPublisher publisher;
 @Override
 public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
 this.publisher = applicationEventPublisher;
 }
 public void publish(ApplicationEvent event) {
 publisher.publishEvent(event);
 }
}
  • 編寫監聽類,只需要在監聽方法上添加eventListener註解,參數爲需要監聽的事件類即可
  • 如果需要異步處理,添加Async註解
  • 可以通過Async的value屬性來選擇線程池。推薦選擇自定義線程池,默認線程池沒有限制線程數量,可能導致OOM
@Componentpublic class HandlingListener {
 @EventListener
 @Async("eventThread")
 public void processHandlingEvent(HandlingEvent event) {
 // 處理事件
 }
  • 調用EventPublisher的publish方法,即可進行事件的發佈
eventPublisher.publish(new HandlingEvent(this, "move"));

整體的流程如下:

  • EventPublisher添加HandlingEvent
  • HandlingListener監聽到了HandlingEvent,執行對應的方法

我們看下Spring中如何實現這個流程的。

Spring事件實現

領域設計:領域事件

 

  • Spring啓動時,EventListenerMethodProcessor會將標識了EventListener註解的方法添加到Context中。這裏就是HandlingListener中的processHandlingEvent方法
for (Method method : annotatedMethods.keySet()) {
 for (EventListenerFactory factory : factories) {
 if (factory.supportsMethod(method)) {
 Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
 ApplicationListener<?> applicationListener =
 factory.createApplicationListener(beanName, targetType, methodToUse);
 if (applicationListener instanceof ApplicationListenerMethodAdapter) {
 ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
 }
 context.addApplicationListener(applicationListener);
 break;
 }
 }
}
  • AbstractApplicationContext實現了ApplicationContext接口,其中維護了掃描到的ApplicationListener
@Overridepublic void addApplicationListener(ApplicationListener<?> listener) {
 Assert.notNull(listener, "ApplicationListener must not be null");
 if (this.applicationEventMulticaster != null) {
 this.applicationEventMulticaster.addApplicationListener(listener);
 }
 this.applicationListeners.add(listener);
}
  • 在EventPublisher發佈事件時,觸發ApplicationEventPublisher的publishEvent方法
  • ApplicationContext接口繼承了ApplicationEventPublisher接口,最終觸發AbstractApplicationContext的publishEvent方法,將事件廣播出去

ApplicationContext接口爲什麼要繼承ApplicationEventPublisher接口?

因爲Spring中定義了幾個默認的事件ContextRefreshedEvent,ContextStartedEvent,ContextStoppedEvent,ContextClosedEvent,RequestHandledEvent,而ApplicationContext就是這些事件的布者。

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
 ......
 else {
 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); // 廣播事件
 }
 .....
}
  • 最終觸發SimpleApplicationEventMulticaster的multicastEvent方法
@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
 for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
 Executor executor = getTaskExecutor();
 if (executor != null) {
 executor.execute(() -> invokeListener(listener, event));
 }
 else {
 invokeListener(listener, event);
 }
 }
}
  • 獲取ApplicationListener,然後使用Executor去執行listener

參考資料

  • Spring源碼
  • 《領域驅動設計:軟件核心複雜性應對之道》
  • 《實現領域驅動設計》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章