源码解析-Abp vNext丨LocalEventBus
前言
基础篇已经更新完了,从本篇开始我们进入,中级篇(学习部分源代码)我会挑一些我个人认为比较重要的知识点结合部分开源项目进行源码讲解,咱们废话不说直接上车。
Abp vNext的事件总线分2种,一种是本地事件总线,一种是分布式事件总线,本节主要讲解本地事件总线,下一节讲分布式事件总线。
事件总线所处模块在 Volo.Abp.EventBus 中。

正文
看Abp源代码我们先从Module下手,这块代码实现的比较简单,就是在类注册的时候判断是否继承ILocalEventHandler/IDistributedEventHandler,将其记录下来并赋值给对应的Options配置类当中。

也就是说当我们定义如下Handler的时候,会在依赖注入的时候,都会将其类型 (Type) 添加到事件总线的配置类当中。
public class ProjectDeleteHandler : ILocalEventHandler<ProjectDeleteEvent>,
ITransientDependency
{
// ...
}
在单元测试EventBusTestBase类我们找一个LocalEventBus的调用类,直接看PublishAsync的调用来自于IEventBus,根据继承我们先找到ILocalEventBus,也就是下面这些,这里注意EventBusBase类型,Abp有分布式事件总线和本地事件总线,这里抽离了一个事件基类提供使用。
public interface IEventBus
{
/// <summary>
/// Triggers an event.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="eventData">Related data for the event</param>
/// <param name="onUnitOfWorkComplete">True, to publish the event at the end of the current unit of work, if available</param>
/// <returns>The task to handle async operation</returns>
Task PublishAsync<TEvent>(TEvent eventData, bool onUnitOfWorkComplete = true)
where TEvent : class;
/// <summary>
/// Triggers an event.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="eventData">Related data for the event</param>
/// <param name="onUnitOfWorkComplete">True, to publish the event at the end of the current unit of work, if available</param>
/// <returns>The task to handle async operation</returns>
Task PublishAsync(Type eventType, object eventData, bool onUnitOfWorkComplete = true);
/// ......
}

还是顺着我们的PublishAsync向下看,我们可以看到调用了TriggerHandlersAsync函数名字就是触发处理器这个Trigger感觉有点js的味道.
public virtual async Task PublishAsync(LocalEventMessage localEventMessage)
{
await TriggerHandlersAsync(localEventMessage.EventType, localEventMessage.EventData);
}
TriggerHandlersAsync内部主要就是调用GetHandlerFactories获取事件工厂,而GetHandlerFactories内部的数据来自于HandlerFactories
HandlerFactories的数据内容在LocalEventBus构造函数中通过调用SubscribeHandlers函数进行填充,最后在TriggerHandlerAsync中完成调用。
protected override IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType)
{
var handlerFactoryList = new List<EventTypeWithEventHandlerFactories>();
foreach (var handlerFactory in HandlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key)))
{
handlerFactoryList.Add(new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value));
}
return handlerFactoryList.ToArray();
}
var method = typeof(ILocalEventHandler<>)
.MakeGenericType(eventType)
.GetMethod(
nameof(ILocalEventHandler<object>.HandleEventAsync),
new[] { eventType }
);
await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData }));
在整套的运行中还牵扯到异步等待,异常处理,工作单元,构造事件处理器工厂,事件销毁等。不过核心的处理顺序就这样。
解析
这里也想大家推荐本系列的讲解仓库Dppt(https://github.com/MrChuJiu/Dppt/tree/master/src)该仓库源于Easy.Core.Flow是我20年创建的用于对知识点的解析,后面代码都会上传到这个仓库.
接下来对上面的Abp代码进行一个简单的分析,简单来看本地EventBus就是通过某个key来反射调用某个函数.简单点直接开整.
我们也写一个IEventBus然后让LocalEventBus继承IEventBus,这里我不需要EventBusBase和ILocalEventBus因为我就是一个本地evetbus的包,总代码300行左右我就不全部拿上来了,大家可以去仓库自己下载。
public interface IEventBus
{
Task PublishAsync<TEvent>(TEvent eventData);
void Unsubscribe(Type eventType, IEventHandlerFactory factory);
}
public class LocalEventBus : IEventBus
{
public async Task PublishAsync<TEvent>(TEvent eventData)
{
await TriggerHandlersAsync(typeof(TEvent), eventData);
}
protected virtual async Task TriggerHandlersAsync(Type eventType, object eventData)
{
foreach (var handlerFactories in GetHandlerFactories(eventType))
{
foreach (var handlerFactory in handlerFactories.EventHandlerFactories)
{
await TriggerHandlerAsync(handlerFactory, handlerFactories.EventType, eventData);
}
}
}
protected virtual async Task TriggerHandlerAsync(IEventHandlerFactory asyncHandlerFactory, Type eventType, object eventData)
{
using (var eventHandlerWrapper = asyncHandlerFactory.GetHandler())
{
var handlerType = eventHandlerWrapper.EventHandler.GetType();
var method = typeof(ILocalEventHandler<>)
.MakeGenericType(eventType)
.GetMethod(
nameof(ILocalEventHandler<object>.HandleEventAsync),
new[] { eventType }
);
await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData }));
}
}
}
因为我们我们既没有模块化也没Autofac我们就简单点来注册Handler,让外部把Handler告诉我们,当然你扫描程序集也是可以的,但是没必要,这里我们的步骤等于Abp在模块里面的配置Options。
public static class DpptEventBusRegistrar
{
public static void AddDpptEventBus(this IServiceCollection services, List<Type> types) {
services.AddSingleton<IEventBus, LocalEventBus>();
var localHandlers = types;/*.Where(s => typeof(ILocalEventHandler<>).IsAssignableFrom(s)).ToList();*/
foreach (var item in localHandlers)
{
services.AddTransient(item);
}
services.Configure<LocalEventBusOptions>(options =>
{
options.Handlers.AddIfNotContains(localHandlers);
});
}
}
源码里面要注意好以下几点,EventBus是单例的因为全局引用,但是Handler是瞬时,因为每次执行完就可以了。
测试
新建一个空项目注册一个Handler.测试结果放在下面了


public class WeatherQueryHandler : ILocalEventHandler<WeatherQueryEto>
{
public Task HandleEventAsync(WeatherQueryEto eventData)
{
Console.WriteLine(eventData.Name);
return Task.CompletedTask;
}
}
public class WeatherQueryEto
{
public string Name { get; set; }
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
// 测试发送
_eventBus.PublishAsync(new WeatherQueryEto() { Name = "测试"+ rng.Next(-20, 55) });
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
结语
本次挑选了一个比较简单的示例来讲,整个EventBus我应该分成3篇 下一篇我来讲分布式EventBus。
最后欢迎各位读者关注我的博客, https://github.com/MrChuJiu/Dppt/tree/master/src 欢迎大家Star
另外这里有个社区地址(https://github.com/MrChuJiu/Dppt/discussions),如果大家有技术点希望我提前档期可以写在这里,希望本项目助力我们一起成长
源码解析-Abp vNext丨LocalEventBus的更多相关文章
- 源码解析-Abp vNext丨分布式事件总线DistributedEventBus
前言 上一节咱们讲了LocalEventBus,本节来讲本地事件总线(DistributedEventBus),采用的RabbitMQ进行实现. Volo.Abp.EventBus.RabbitMQ模 ...
- abp vnext2.0核心组件之.Net Core默认DI组件切换到AutoFac源码解析
老版Abp对Castle的严重依赖在vnext中已经得到了解决,vnext中DI容器可以任意更换,为了实现这个功能,底层架构相较于老版abp,可以说是进行了高度重构.当然这得益于.Net Core的D ...
- abp vnext2.0核心组件之领域实体组件源码解析
接着abp vnext2.0核心组件之模块加载组件源码解析和abp vnext2.0核心组件之.Net Core默认DI组件切换到AutoFac源码解析集合.Net Core3.1,基本环境已经完备, ...
- 语义分割丨PSPNet源码解析「测试阶段」
引言 本文接着上一篇语义分割丨PSPNet源码解析「网络训练」,继续介绍语义分割的测试阶段. 模型训练完成后,以什么样的策略来进行测试也非常重要. 一般来说模型测试分为单尺度single scale和 ...
- .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析
介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
随机推荐
- JAVA 之 每日一记 之 算法( 给定一个正整数,返回它在 Excel 表中相对应的列名称。 )
题目: 给定一个正整数,返回它在 Excel 表中相对应的列名称. 例如: 1 -> A 2 -> B 3 -> C ... 26 -> Z 27 -> AA 28 -& ...
- vue 封装 axios 和 各类的请求,以及引入 .vue 文件中使用
//src 底下建立 api 文件夹 // api 文件夹下建立 request,js 文件,文件内容复制下面这段代码即可 /** * ajax请求配置 */ import axios fro ...
- SSL基础知识及Nginx/Tomcat配置SSL
HTTPS 是在 HTTPS 基础之上添加 SSL/TLS 使网络通讯加密,进而确保通信安全.可简记为 HTTPS = HTTP + SSL/TLS 本文档主要讲解常规SSL格式.Nginx 与 To ...
- noip模拟33
\(\color{white}{\mathbb{失足而坠千里,翻覆而没百足,名之以:深渊}}\) 这场考试的时间分配非常不科学 开题试图想 \(t1\) 正解,一个半小时后还是只有暴力,特别惊慌失措 ...
- JS边角料: NodeJS+AutoJS+WebSocket+TamperMonkey实现局域网多端文字互传
---阅读时间约 7 分钟,复现时间约 15 分钟--- 由于之前一直在用的扩展 QPush 停止服务了,苦于一人凑齐了 Window, Android, Mac, ios 四种系统的设备,Apple ...
- Fastjson 1.2.22-24 反序列化漏洞分析(1)
Fastjson 1.2.22-24 反序列化漏洞分析(1) 前言 FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转 ...
- Learning How to Learn 学习如何学习
Introduction 这是 UCSD 开设在 Coursera 上的课程 Learning How to Learn 的课程笔记.这门课程主要基于神经科学和认知心理学的一些研究成果讲述高效学习的理 ...
- Math.round真的是四舍五入吗?我不这么认为
public static long round(double a) 返回最接近参数的 long.结果将舍入为整数:加上 1/2,对结果调用 floor 并将所得结果强制转换为 long 类型.换句话 ...
- 一些PHP选项参数相关的函数
关于 PHP 的配置,我们大多数情况下都是去查看 php.ini 文件或者通过命令行来查询某些信息,其实,PHP 的一些内置函数也可以帮助我们去查看或操作这些配置参数.比如之前我们学习过的 关于php ...
- 我们也有自带的缓存系统:PHP的APCu扩展
想必大家都使用过 memcached 或者 redis 这类的缓存系统来做日常的缓存,或者用来抗流量,或者用来保存一些常用的热点数据,其实在小项目中,PHP 也已经为我们准备好了一套简单的缓存系统,完 ...