Abp领域事件(EventBus)源码解析
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);
}
}
Abp领域事件(EventBus)源码解析的更多相关文章
- EventBus源码解析 源码阅读记录
EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...
- 【Android】EventBus 源码解析
EventBus 源码解析 本文为 Android 开源项目实现原理解析 中 EventBus 部分项目地址:EventBus,分析的版本:ccc2771,Demo 地址:EventBus Demo分 ...
- abp vnext2.0核心组件之领域实体组件源码解析
接着abp vnext2.0核心组件之模块加载组件源码解析和abp vnext2.0核心组件之.Net Core默认DI组件切换到AutoFac源码解析集合.Net Core3.1,基本环境已经完备, ...
- Android EventBus源码解析 带你深入理解EventBus
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...
- Android EventBus源码解析
项目地址 :https://github.com/greenrobot/EventBus 这个项目个人感觉就是为了解决回调事件过多的,比方说A函数在做完以后 要调用b类的c函数,那我们通常的做法就是 ...
- EventBus源码解析
用例 本文主要按照如下例子展开: //1. 新建bus对象,默认仅能在主线程上对消息进行调度 Bus bus = new Bus(); // maybe singleton //2. 新建类A(sub ...
- [EventBus源码解析] 初探EventBus
本期blog作为EventBus(以下简称EB)学习的始动篇,主要记载了EB的功能.优点.使用方法,内容基于github上的README.md与HOWTO.md. 何为EventBus EB实现了An ...
- [EventBus源码解析] 订阅者处理消息的四种ThreadMode
前言 在前面,我们探讨了如何在自己的代码中引入EventBus,进行基本的事件分发/监听:对注册观察者与事件发送的过程进行了浅析.从之前的学习中,我们了解到,EventBus一共有4种onEvent方 ...
- [EventBus源码解析] EventBus.post 方法详述
前情概要 上一篇blog我们了解了EventBus中register/unregister的过程,对EventBus如何实现观察者模式有了基本的认识.今天我们来看一下它是如何分发一个特定事件的,即po ...
随机推荐
- ES6中对象的扩展
ES6不仅为字符串.数值和数组带来了扩展,也为对象带来了很多新特性.这一节,我们来一起学习一下对象的扩展. 对象的传统表示法 我们回顾一下,对象的传统表示法: let person = { " ...
- 2019-2020-1 20199325《Linux内核原理与分析》第九周作业
第九周作业要求: 理解Linux系统中进程调度的时机,可以在内核代码中搜索schedule()函数,看都是哪里调用了schedule(),判断我们课程内容中的总结是否准确: 使用gdb跟踪分析一个sc ...
- Android:RelativeLayout 内容居中
Android RelativeLayout 内容居中解决办法: 使用Linearlayout本来利用父控件的gravity属性是很好解决的.但是对应RelativeLayout虽然有gravity属 ...
- C/C++ 程序执行时间
C/C++中的计时函数是clock(),而与其相关的数据类型是clock_t.在MSDN中,查得对clock函数定义如下: clock_t clock( void ); 这个函数返回从“开启这个程序进 ...
- Scala的自定义类型标记
Scala的自定义类型标记 Scala中有很多千奇百怪的符号标记,看起来是那么的独特,就像是一杯dry martini-好像黑夜中的萤火虫,那么耀眼,那么出众. 好了言归正传,这一篇文章我们会讲一下S ...
- 【Linux题目】第七关
1. 如果想修改开机内核参数,应该修改哪个文件? A. /dev/sda1 B. /etc/fstab 磁盘自动挂载的文件 C. /boot/grub/grub.conf D. /etc/rc.loc ...
- HTML中使用CSS样式(下)
上节内容回顾加补充: 补充:默认img标签,有一个1px的边框 如果点击图片跳转到连接,a标签下套img标签,在IE有的版本中,会有蓝色边框. <a href="http://blog ...
- Kubernetes 持久化存储是个难题,解决方案有哪些?\n
像Kubernetes 这样的容器编排工具正在彻底改变应用程序的开发和部署方式.随着微服务架构的兴起,以及基础架构与应用程序逻辑从开发人员的角度解耦,开发人员越来越关注构建软件和交付价值. Kuber ...
- 谈谈Java的线程池设计
在实际项目中,如果因为想异步执行暂时性的任务而不断创建线程是很浪费资源的事情(当一个任务执行完后,线程也没用了).这种情况下,最好是将任务提交给线程池执行. 所谓池,就是将管理某一种资源,对资源进行复 ...
- GPTL—练习集—006树的遍历
#include<bits/stdc++.h> using namespace std; typedef int daTp;//datatype typedef struct BTNode ...