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