zoukankan      html  css  js  c++  java
  • 事件总线功能库,Reface.EventBus 详细使用教程

    Reface.AppStarter 中的事件总线功能是通过 Reface.EventBus 提供的。

    参考文章 : Reface.AppStarter 框架初探

    使用 Reface.EventBus ,你可以在 Reface.AppStarter 框架外使用事件总线的功能。

    Reface.EventBus 提供了两个版本的包 Reface.EventBusReface.Core.EventBus ,分别工作在 .Net Framework.Net Core 平台上。

    除了一些细节的不同,这两个版本的使用方法完全相同。

    基本篇

    下面我们一起来了解如何使用 Reface.EventBus定义事件定义监听器注册监听器发布事件

    定义事件

    事件是一个单独类型,它继承于 Reface.EventBus.Event其子类 , 我们先看看 Event 的定义

        public class Event
        {
            public object Source { get; private set; }
    
            public Dictionary<string, object> Context { get; private set; }
    
            public Event(object source)
            {
                if (source == null)
                    throw new ArgumentNullException("source");
                Source = source;
                this.Context = new Dictionary<string, object>();
            }
        }
    

    Event 要求一个 object 类型的构造函数,用来表示事件是由谁发布的,与 .Net 中的 event 模式中的 sender 含义相同。

    属性 Source

    从构造函数可以看出 , 可以通过属性 Source 获取到事件的发布者。

    属性 Context

    正如其名,是事件的上文对象,你可以向其中存取任何你希望的内容。该属性可以成为多个 监听者 之间 沟通 的桥梁。

    使用该属性时需要注意 :

    • 监听者模式情况下无论控制执行顺序,因此要考虑到当前监听者处理时,某些上下文的内容尚未创建
    • 如果你的监听者对上下文有着严格的前后 顺序要求,请为监听器添加优先级的功能 (后文有介绍)

    示例

    我们定义一个事件,可以在控制台程序中,描述一个用户输入了一个命令的事件。

    public class CommandReadEvent : Event
    {
        public string Command { get; private set; }
        public CommandReadEvent(object source, string cmd) : base(source)
        {
            this.Command = cmd;
        }
    }
    

    定义监听器

    所有的监听器必须实现 IEventListener<TEvent> 接口,IEventHandler<T> 的定义如下

    public interface IEventListener<TEvent>
        where TEvent : Event
    {
        void Handle(TEvent @event);
    }
    

    泛型 TEvent

    泛型指定了该监听器监听哪种事件类型的事件。

    职责单一 是重要的设计思想。因此,一件监听器只能针对一个事件类型进行处理。

    方法 Handler

    该方法是事件处理的具体过程,参数就是事件类型。

    同样,我们依照 职责单一 的原则,强烈建议一个监听器内只实现一个任务,多个任务由多个监听器实现。

    监听器在哪儿 ?

    EventBus 无法得知开发者把它们的 监听器 都写在哪儿,所以需要以某种方式发现它们,才能调度它们。

    IEventListenerFinder 就是负责找到这些 监听器 并创建它们实例的组件。

    关于 监听器 的创建

    所有的 监听器 都是通过 IEventListenerFinder 创建的。

    .Net Framework 版本中所有的 IEventListenerFinder 都不支持 IOC / DI , 如果你希望对 监听器 注入外部组件,可以考虑和成熟的 IOC / DI 库集成,比如 AutofacInject 等等。

    .Net Core 版本中,已经实现了与 ServiceCollection 的集成,当你编写 Web
    项目时,你可以在 Startup.ConfigureService 中启用 EventBus 的功能以及对程序集内的所有 监听器 的发现与注册。

    以下是在 .Net Core 版本中,如何启用 EventBus 以及对 监听器 的发现与注册

    services // ServiceCollection 的实例
        .AddEventBus() // 注册运行 EventBus 的必要组件
        .AddEventListeners(typeof(SomeClass).Assembly); // 扫描指定程序集内的 监听器 并注册到容器中
    
    在 .Net Framework 中注册监听器

    强烈建议将你的 IEventListenerFinderIOC / DI 容器集成。

    但是,如果你没有这样的需求,你也可以通过以下两种方式向 EventBus 注册你的 监听器

    1. 通过 config 文件注册监听器

    首先,将 config* 文件添加 section 节点,建议使用 eventBus 作为 name 的值。

    <section name="eventBus" type="Reface.EventBus.Configuration.EventBusSection, Reface.EventBus"/>
    

    接下来在 configuration 节点内,添加 eventBus 节点

    <configuration>
      <eventBus>
        <listeners>
          <add type="Demo.Listeners.OnConsoleStarted02, Demo" />
          <add type="Demo.Listeners.OnConsoleStarted01, Demo" />
          <add type="更多的监听器类型名称" /> 
        </listeners>
      </eventBus>
    </configuration>
    

    类型名称应由 类型全名+逗号+程序集名 组成。

    配置文件是 EventBus 默认的选项,你不需要做任何额外的编码,就可以发布事件了。

    IEventBus eventBus = new EventBus();
    eventBus.Publish(new MyEvent());
    

    EventBus 发布事件时,会通过 Activator.CreateInstance(Type) 的方式创建这些 监听者,因此你的 监听者 的构造函数不可以有参数。

    2. 扫描程序集以发现监听器

    通过这种方式,你可以指定一些程序集,EventBus 会预先扫描它们,并将实现了 IEventListener 接口的类型记录。

    config 模块相同,监听器 是通过 Activator.CreateInstance(Type) 的方式创建的,因此 监听者 的构造函数依然不可以有参数。

    发布事件

    IEventBus 是发布事件的组件,它只包含一个成员,Publish 用于发布事件。

    public interface IEventBus
    {
        void Publish(Event @event);
    }
    

    我会在未来的版本中加入 AsyncPublish(Event) 方法,以用于异步发布事件。

    库中只有一个 IEventBus 的实现类 : EventBus

    该类是依赖 IEventListenerFinder 发现 监听器 并发布 事件

    你也可以实现属于你自己的 IEventBus ,你可以让它通过 MQ 等其它方式来发布事件。

    扩展篇

    监听者的执行顺序

    一个 Event 可以触发多个 监听器 的执行。在正常情况下,EventBus 只以 注册的顺序扫描程序集的顺序 来决定 监听器 的执行顺序。

    在实际开发环境中,如果你对 监听器 的执行顺序有要求,比如,AListener 必须早于 BListener , 你可以选择以下两个解决方案

    • 设计一个新的事件,当 AListener 完成后触发,BListener 监听新的事件
    • AListenerBListener 实现优先级接口,并让 AListener 优于 BListener

    库内有接口 IPrioritized , 它可以让你的 监听器 额外提供一个 优先级 的属性,这是一个 int 型的属性,数值越小,就越先执行。

    对于未实现 IPrioritized监听器,优先级一律是 int.MaxValue

    public class AListener : IEventListener<SomeEvent>, IPrioritized
    {
        public int Priority => 0;
    
        public void Handle(SomeEvent @event)
        {
            Console.WriteLine("Console Started 01");
        }
    }
    

    Event 的继承

    使用 Reface.EventBus ,你可以设计具有继承关系的 Event 类型。

    比如

    // 删除数据的事件
    public class DataDeletingEvent : Event {}
    
    // 删除用户的事件
    public class UserDeletingEvent : DataDeletingEvent {}
    
    // 删除部门的事件
    public class DeptDeletingEvent : DataDeletingEvent {}
    

    当你对事件监听时,你可以编写一个 监听器 来监听 DataDeletingEvent

    public class DataDeletingEventListener : IEventListener<DataDeletingEvent>
    {
    
    }
    

    该处理器可以处理所有 DataDeletingEvent其子类 的事件。

    运行机制

    Publish(TEvent) 时,都发生了什么事 ?

    1. 调用 IEventListenerFinder.CreateAllEventListeners() 获取所有事件 处理器
    2. 对所有的 处理器 进行排序
    • 没有标记 IPrioritized 的处理器优先级为 int.MaxValue
    • 排序按 Priority 从小到大排序
    1. 缓存中获取分析 哪些 处理器 将参与此次 事件 的处理
    2. 对参与此次 事件处理器 从缓存或创建 DynamicMethod 用于执行 Handle 方法 , DynamicMethod 是通过 Emit 创建的,因此执行速度是非常快的
    3. 执行所有的 DynamicMethod

    相关链接

  • 相关阅读:
    Android对包名和类名是否存在的判断
    Android权限表
    论艺术的背后还有纪律
    jeecgboot/IDEA中 debug不行,run可以
    java集合类
    HashMap、HashTable、LinkedHashMap和TreeMap用法和区别
    protobuf序列化算法原理
    谁能帮我看看着究竟是什么问题呀,我在本机运行都是好好的,但在别人的机子上运行就出这个呢?我快疯了!!!
    VS删除空白行
    POJ 2516 Minimum Cost
  • 原文地址:https://www.cnblogs.com/ShimizuShiori/p/12813422.html
Copyright © 2011-2022 走看看