前言

基础篇已经更新完了,从本篇开始我们进入,中级篇(学习部分源代码)我会挑一些我个人认为比较重要的知识点结合部分开源项目进行源码讲解,咱们废话不说直接上车。

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的更多相关文章

  1. 源码解析-Abp vNext丨分布式事件总线DistributedEventBus

    前言 上一节咱们讲了LocalEventBus,本节来讲本地事件总线(DistributedEventBus),采用的RabbitMQ进行实现. Volo.Abp.EventBus.RabbitMQ模 ...

  2. abp vnext2.0核心组件之.Net Core默认DI组件切换到AutoFac源码解析

    老版Abp对Castle的严重依赖在vnext中已经得到了解决,vnext中DI容器可以任意更换,为了实现这个功能,底层架构相较于老版abp,可以说是进行了高度重构.当然这得益于.Net Core的D ...

  3. abp vnext2.0核心组件之领域实体组件源码解析

    接着abp vnext2.0核心组件之模块加载组件源码解析和abp vnext2.0核心组件之.Net Core默认DI组件切换到AutoFac源码解析集合.Net Core3.1,基本环境已经完备, ...

  4. 语义分割丨PSPNet源码解析「测试阶段」

    引言 本文接着上一篇语义分割丨PSPNet源码解析「网络训练」,继续介绍语义分割的测试阶段. 模型训练完成后,以什么样的策略来进行测试也非常重要. 一般来说模型测试分为单尺度single scale和 ...

  5. .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析

    介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...

  6. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  7. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  8. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  9. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

随机推荐

  1. 手动编译部署LNMP环境(CentOS7.5+Nginx-1.18.0+MySQL-5.7.30+PHP-7.4.14)

    在平时运维工作中,经常需要用到LNMP应用框架.LNMP环境是指在Linux系统下,由Nginx + MySQL + PHP组成的网站服务器架构. 可参考前面的文章: 如何在CentOS 7上搭建LA ...

  2. Gram-Schmidt图像融合

    遥感图像融合的定义是通过将多光谱低分辨率的图像和高分辨率的全色波段进行融合从而得到信息量更丰富的遥感图像.常用的遥感图像融合方法有Brovey\PCA\Gram-Schmidt方法.其中Gram-Sc ...

  3. Python - 面向对象编程 - 实战(4)

    需求:士兵突进 士兵许三多有一把 AK47 士兵可以开火 枪能够发射子弹 枪装填子弹,可以增加子弹数量 需求分析 很明显有两个类:士兵类,枪类 AK47 是枪名,是枪类的属性,每把枪都有子弹数,所以子 ...

  4. Docker - 解决 docker push 上传镜像报:denied: requested access to the resource is denied 的问题

    问题背景 在 Linux 已登录自己的 Docker hub 账号 上传本地镜像但是报错了 docker push tomcat 解决方案 docker tag tomcat poloyy/tomca ...

  5. 分组概念&贪婪与懒惰

    分组概念&贪婪与懒惰 1.分组 2.贪婪和懒惰 3.懒惰 4.处理选项 5.实例:百度搜索结果页面源码中获取当前页的10个标题 5.1页面源码分析规律 5.2正则表达式,匹配出10个标题 这是 ...

  6. jquery实现强制刷新

    $('iframe.active').attr('src', $('iframe.active').attr('src'));

  7. MySQL高级语句(二)

    目录: 1.别名 2.子查询 3.EXISTS 4.连接查询 5.CREATE VIEW 视图 6.UNION 联集 7.交集值 8.无交集值 9.CASE 10.算排名 11.算中位数 12.算累积 ...

  8. tomcat服务字符编码改为UTF-8

    -Dfile.encoding=UTF-8 --仅供参考

  9. Java Web下MySQL数据库的增删改查(二)

    前文:https://www.cnblogs.com/Arisf/p/14095002.html 在之前图书管理系统上做了改进优化 图书管理系统v2 首先是项目结构: 1.数据库的连接: 1 pack ...

  10. 在PHP中操作文件的扩展属性

    在操作系统的文件中,还存在着一种我们可以自己定义的文件属性.这些属性不是保存在文件内容中,也不是直接可以通过 ls -al 所能看到的内容.它们可以将一个键值对信息永久得关联到文件上,一般现在的 Li ...