1. 程式人生 > >Abp領域事件(EventBus)原始碼解析

Abp領域事件(EventBus)原始碼解析

Abp中使用EventBus來解耦領域中的業務邏輯,也是訂閱-釋出模式的一種實現。簡單來說就是,當我觸發一個事件,註冊了這個事件的處理器就會被找到並執行。 先看看整體程式碼結構 ![1589532666909](https://img2020.cnblogs.com/blog/1261088/202005/1261088-20200518152704580-783373080.png) 其中`Entities`資料夾中是對於實體相關的領域事件的實現與本章主題無關,我們就可以先當他不存在了。 可以看到有四個東西我們需要注意 `EventData` 這個我們可以就當作是事件型別,需要觸發的事件就是這個東西。 `EventHandler` 事件處理器。當有事件觸發的時候,如果處理器註冊了這個事件那麼會來執行這個處理器 `EventHandlerFactory` 事件處理器工廠。 維護事件處理器的新建,獲取和銷燬。一個事件處理器對應一個事件處理器工廠 `EventBus` 負責註冊,取消註冊和觸發事件 我們把這四個東西聯合起來描述下領域事件的流程就是這樣的(一個例子): 1. 我定義了一個訂單建立成功的事件 `OrderCreated_EventData` 2. 當訂單建立成功,我需要傳送郵件,那麼我建立一個處理器,`SendMailEventHandler` 3. 將`SendMailEventHandler`包裝到一個工廠中,並 和`OrderCreated_EventData`一起註冊到`EventBus`裡面 4. 通過`EventBus`觸發事件`OrderCreated_EventData`,那麼就會執行已經`SendMailEventHandler` 我們會一個一個來看下這個幾個東西 ------ #### EventData 我們先來看下`IEventData`和`IEventDataWithInheritableGenericArgument` ```C# /// /// Defines interface for all Event data classes. /// public interface IEventData { /// /// The time when the event occured. /// DateTime EventTime { get; set; } /// /// The object which triggers the event (optional). /// object EventSource { get; set; } } ``` ```c# /// /// 當我們的eventdata型別是隻有一個泛型引數的並且該引數是需要用來繼承的時候,我們需要實現這個介面。 /// 舉個例子,我們有一個Student繼承Person,當觸發一個EventData{Student}的事件時,我希望EventData{Person}也被觸發那麼我就需要實現IEventDataWithInheritableGenericArgument這個介面 ///
public interface IEventDataWithInheritableGenericArgument { /// /// Gets arguments to create this class since a new instance of this class is created. /// /// Constructor arguments object[] GetConstructorArgs(); } ``` `IEventData` 很簡單隻有兩個屬性 `EventTime`和`EventSource` ,`EventTime`是事件觸發的時間,`EventSource`是觸發這個事件的物件,是可選。 關於`IEventDataWithInheritableGenericArgument` 我在程式碼裡面寫了備註了。 接下來看下`EventData` ```C# /// /// Implements
and provides a base for event data classes. ///
[Serializable] public abstract class EventData : IEventData { /// /// The time when the event occurred. /// public DateTime EventTime { get; set; } /// /// The object which triggers the event (optional). ///
public object EventSource { get; set; } /// /// Constructor. /// protected EventData() { EventTime = Clock.Now; } } ``` `EventData`實現了`IEventData`,在建構函式中對欄位`EventTime`進行了賦值。 總的來說`EventData`沒有多少東西,就是定義了事件本身。 #### EventHandler 我們先來看下`IEventHandler` ```C# /// /// Undirect base interface for all event handlers. /// Implement instead of this one. /// public interface IEventHandler { } ``` `IEventHandler`不是直接用來作為介面讓handler繼承的,handler繼承的是用`EventData`作為泛型引數的`IEventHandlerOfTEventData`和`IAsyncEventHandlerOfTEventData`,`IEventHandler`更多的是用來統一`IEventHandlerOfTEventData`和`IAsyncEventHandlerOfTEventData`,以方便進行判斷。 ```C# public interface IEventHandler : IEventHandler { /// /// Handler handles the event by implementing this method. /// /// Event data void HandleEvent(TEventData eventData); } public interface IAsyncEventHandler : IEventHandler { /// /// Handler handles the event by implementing this method. /// /// Event data Task HandleEventAsync(TEventData eventData); } ``` `IEventHandlerOfTEventData`和`IAsyncEventHandlerOfTEventData`都是繼承於`IEventHandler`的泛型型別,泛型引數是`EventData`,都只有一個`HandleEvent`的方法,區別在於一個是同步一個是非同步。而`HandleEvent` 就是處理器處理事件時需要執行的方法。我們如果需要新增一個`handler`就需要繼承`IEventHandlerOfTEventData`或者`IAsyncEventHandlerOfTEventData`,泛型引數使用`EventData`,並且實現`HandleEvent`的方法。 #### EventHandlerFactory `Abp`使用`Factory`來包裝`EventHandler`,首先看下`IEventHandlerFactory` ```C# /// Defines an interface for factories those are responsible to create/get and release of event handlers. /// 用於handler的建立 獲取 和釋放 抽象這個接口出來是因為我們可以選擇不同的方式來獲取和管理EventHandler /// public interface IEventHandlerFactory { /// /// Gets an event handler. /// /// The event handler IEventHandler GetHandler(); /// /// Gets type of the handler (without creating an instance). /// /// Type GetHandlerType(); /// /// Releases an event handler. /// /// Handle to be released void ReleaseHandler(IEventHandler handler); } ``` 很簡單的三個介面,`GetHandler`用於獲取handler,如果不存在就是建立並返回。`ReleaseHandler` 用於釋放`Handler`,`GetHandlerType`用於在未例項化的時候直接獲取到`handler`的型別。 `Abp`提供了一個預設的實現`IocHandlerFactory`,是基於`IOC`來實現介面的功能,具體程式碼比較簡單也不貼了。 #### EventBus `EventBus`是最重要的型別,負責註冊,取消註冊和觸發事件。我們分別來看一下 ##### 註冊事件 `EventBus`提供了很多註冊的過載方法,都是為了不同場景的註冊,我們直接看最根本的方法 ```C# public IDisposable Register(Type eventType, IEventHandlerFactory factory) { GetOrCreateHandlerFactories(eventType) .Locking(factories => factories.Add(factory)); return new FactoryUnregistrar(this, eventType, factory); } private List GetOrCreateHandlerFactories(Type eventType) { return _handlerFactories.GetOrAdd(eventType, (type) => new List()); } /// /// All registered handler factories. /// Key: Type of the event /// Value: List of handler factories /// private readonly ConcurrentDictionary> _handlerFactories; ``` `_handlerFactories` 是一個執行緒安全的字典,Key是事件的型別也就是`EventData`的型別,value是一個`EventHandlerFactory`的List。一個事件型別可能有多個來處理的`Handler`,所有對應一個`EventHandlerFactory` 的列表,比較簡單。 ##### 取消註冊 取消註冊就跟註冊是一個逆向的過程,從`EventType`對應的`EventHandlerFactory`的List中移除指定的`Factory`。 ##### 觸發事件 觸發事件主要做了兩件事情 第一個迴圈呼叫`EventType` 對應`Handler` 列表中的每一個`handler`的`HandleEvent`方法。只貼一下重要的地方,我把一些地方也加上了備註 ```C# foreach (var handlerFactories in GetHandlerFactories(eventType)) { foreach (var handlerFactory in handlerFactories.EventHandlerFactories) { var handlerType = handlerFactory.GetHandlerType(); if (IsAsyncEventHandler(handlerType)) { AsyncHelper.RunSync(() => TriggerAsyncHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions)); } else if (IsEventHandler(handlerType)) { TriggerHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions); } else { var message = $"Event handler to register for event type {eventType.Name} does not implement IEventHandler<{eventType.Name}> or IAsyncEventHandler<{eventType.Name}> interface!"; exceptions.Add(new AbpException(message)); } } } ``` ```C# if (eventHandler == null) { throw new ArgumentNullException($"Registered event handler for event type {eventType.Name} is null!"); } //構建一個IEventHandler的型別 var handlerType = eventHandler.GetType(); //根據指定的引數型別eventType獲取方法HandleEvent var method = handlerType.GetMethod( "HandleEvent", new[] { eventType } ); //指定eventData作為引數執行方法HandleEvent method.Invoke(eventHandler, new object[] { eventData }); ``` 第二件事就是上面所說`IEventDataWithInheritableGenericArgument`這個介面,也就是判斷我們的事件型別是不是繼承`IEventDataWithInheritableGenericArgument`並且是隻有一個泛型引數的泛型型別,如果是的話,我們需要找到泛型引數的父級來觸發事件,當然父級被觸發了,父級的父級也會觸發。貼一下重要程式碼 ```c# if (eventType.GetTypeInfo().IsGenericType && eventType.GetGenericArguments().Length == 1 && typeof(IEventDataWithInheritableGenericArgument).IsAssignableFrom(eventType)) { //獲取事件型別的泛型引數 比如EventData中的Student var genericArg = eventType.GetGenericArguments()[0]; //獲取泛型引數的直接繼承的父級 比如Person var baseArg = genericArg.GetTypeInfo().BaseType; if (baseArg != null) { //根據父級的泛型引數構造一個以父級泛型引數作為泛型引數的型別 比如EventData var baseEventType = eventType.GetGenericTypeDefinition().MakeGenericType(baseArg); //獲取當前eventData的建構函式的引數值,按照Abp的預設實現,就是泛型本身的物件,比如EventData 例項中Student這個物件 var constructorArgs = ((IEventDataWithInheritableGenericArgument)eventData).GetConstructorArgs(); //通過上面構造的型別和拿到的建構函式的引數值,例項化一個物件,也就是EventData例項化一個物件 var baseEventData = (IEventData)Activator.CreateInstance(baseEventType, constructorArgs); baseEventData.EventTime = eventData.EventTime; //觸發這個EventData例項化的物件也可以叫做事件 Trigger(baseEventType, eventData.EventSource, baseEventData); }