Abp领域事件(EventBus)源码解析

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);
                }
            }
posted @ 2020-05-18 15:30  RstarYan  阅读(1043)  评论(0编辑  收藏  举报
$(function(){ $('#blogTitle h1').addClass('bounceInLeft animated'); $('#blogTitle h2').addClass('bounceInRight animated'); // 删除反对按钮 $('.buryit').remove(); initCommentData(); }); function initCommentData() { $('.feedbackItem').each(function() { var text = $(this).find('.feedbackListSubtitle .layer').text(); // 将楼层信息放到data里面 // $(this).find('.blog_comment_body').attr('data-louceng', text.replace(/^#/g, '')); if($(this).find('.feedbackListSubtitle .louzhu').length>0) $(this).addClass('myself'); var avatar = $(this).find('> .feedbackCon > span').html() || 'https://pic.cnblogs.com/face/sample_face.gif'; $(this).find('> .feedbackCon > .blog_comment_body').append('') }); } $(document).ajaxComplete(function(event, xhr, settings) { // 监听获取评论ajax事件 if(settings.url.indexOf('/mvc/blog/GetComments.aspx') >= 0) { initCommentData(); } });