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

RstarYan發表於2020-05-18

Abp中使用EventBus來解耦領域中的業務邏輯,也是訂閱-釋出模式的一種實現。簡單來說就是,當我觸發一個事件,註冊了這個事件的處理器就會被找到並執行。

先看看整體程式碼結構

1589532666909

其中Entities資料夾中是對於實體相關的領域事件的實現與本章主題無關,我們就可以先當他不存在了。

可以看到有四個東西我們需要注意

EventData 這個我們可以就當作是事件型別,需要觸發的事件就是這個東西。

EventHandler 事件處理器。當有事件觸發的時候,如果處理器註冊了這個事件那麼會來執行這個處理器

EventHandlerFactory 事件處理器工廠。 維護事件處理器的新建,獲取和銷燬。一個事件處理器對應一個事件處理器工廠

EventBus 負責註冊,取消註冊和觸發事件

我們把這四個東西聯合起來描述下領域事件的流程就是這樣的(一個例子):

  1. 我定義了一個訂單建立成功的事件 OrderCreated_EventData
  2. 當訂單建立成功,我需要傳送郵件,那麼我建立一個處理器,SendMailEventHandler
  3. SendMailEventHandler包裝到一個工廠中,並 和OrderCreated_EventData一起註冊到EventBus裡面
  4. 通過EventBus觸發事件OrderCreated_EventData,那麼就會執行已經SendMailEventHandler

我們會一個一個來看下這個幾個東西


EventData

我們先來看下IEventDataIEventDataWithInheritableGenericArgument

 /// <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 很簡單隻有兩個屬性 EventTimeEventSourceEventTime是事件觸發的時間,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作為泛型引數的IEventHandlerOfTEventDataIAsyncEventHandlerOfTEventData,IEventHandler更多的是用來統一IEventHandlerOfTEventDataIAsyncEventHandlerOfTEventData,以方便進行判斷。

  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);
    }

IEventHandlerOfTEventDataIAsyncEventHandlerOfTEventData都是繼承於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 用於釋放HandlerGetHandlerType用於在未例項化的時候直接獲取到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 列表中的每一個handlerHandleEvent方法。只貼一下重要的地方,我把一些地方也加上了備註

 
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);
                }
            }

相關文章