源码解析-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源码解析 前面 ...
随机推荐
- GUI常用监听事件
概念 对鼠标.键盘等一系列事件做出相应的反馈 事件监听 //创建监听事件 public class Demo { public static void main(String[] args) { Fr ...
- vue 接入 vod-js-sdk-v6.js 完成视频上传
东西有点多,耐心看完.按照操作一步一步来,绝对能成功 首先:npm 引入 npm install vod-js-sdk-v6 mian.js 全局引入 //腾讯云点播 import TcVod f ...
- vue post 请求 是 request payload 而不是 FromData ,以及 格式转换成 FromData 需要的 key value 格式
export function 方法名字(传进来要给后端的参数){ return request({ url : ' 后端提供的接口路径 ', method ...
- 基于Vue+Vuex+Vue-Router+axios+mint-ui的移动端电商项目
第一步:安装Node 1.打开NodeJS的官网,下载和自己系统相配的NodeJS的安装程序,包括32位还是64位一定要选择好,否则会出现安装问题. 下载地址:https://nodejs.org/e ...
- python 回归分析
一.线性回归 1 绘制散点图 import matplotlib.pyplot as plt x = [5,7,8,7,2,17,2,9,4,11,12,9,6] y = [99,86,87,88,1 ...
- window创建l2tp
windows上创建一个L2TP的隧道连接 进入控制面板,打开"网络和共享中心",如下图,之后点击"设置新的连接或网络" 进入到"设置连接或网络&qu ...
- Input 只能输入数字,数字和字母等的正则表达式
JS只能输入数字,数字和字母等的正则表达式 1.文本框只能输入数字代码(小数点也不能输入) <input onkeyup="this.value=this.value.replace( ...
- 全网唯一正常能用的centos7 安装mysql5.7.35 22 33 25
CentOS7.4用yum安装并配置MySQL5.7 1.配置YUM源 下载MySQL源安装包 wget http://dev.mysql.com/get/mysql57-community-re ...
- POJ1426——Find The Multiple
POJ1426--Find The Multiple Description Given a positive integer n, write a program to find out a non ...
- Java实现导出Excel
项目中实现的代码: 一.//controller @RequestMapping(value = "/exportAccountManager") public void expo ...