ABP之事件总线
Posted 善良的小赵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ABP之事件总线相关的知识,希望对你有一定的参考价值。
前面已经对Castle Windsor的基本使用进行了学习,有了这个基础,接下来我们将把我们的事件总线再次向ABP中定义的事件总线靠近。从源码中可以知道在ABP中定义了Dictionary,存放三种类型的Factory,然后通过容器的方式实例化相应的handlerfactory。承接前面的随笔,我们为什么要使用IOC?
IOC是用来代替反射的。那么反射在我们EventBus中有什么功能?反射是用来创建handler的实例的。那么我们的容器其实就是用来初始化实例这么一点功能的,所以我们不需要过多的改动我们以前的代码,只需加入容器的注入,同时在初始化的时候将反射的方式改为容器的方式。
IOCEventBus(第一种)
using Castle.MicroKernel.Registration; using Castle.Windsor; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EventBus { public class IOCEventBus : IEventBus { private IOCEventBus() { IocContainer = new WindsorContainer(); mapDic = new ConcurrentDictionary<Type, List<Type>>(); } //EventBus单例模式 public static IOCEventBus Default = new IOCEventBus(); public IWindsorContainer IocContainer { get; private set; } private ConcurrentDictionary<Type, List<Type>> mapDic; public void Register<TEventData>(Type handlerType) where TEventData : IEventData { //将数据存储到mapDic var dataType = typeof(TEventData); Register(dataType, handlerType); } public void Register(Type dataType, Type handlerType) { //注册IEventHandler<T>到IOC容器 var handlerInterface = handlerType.GetInterface("IEventHandler`1"); if (!IocContainer.Kernel.HasComponent(handlerInterface)) {
IocContainer.Register(Component.For(handlerInterface).ImplementedBy(handlerType));
} //放入总线中 if (mapDic.Keys.Contains(dataType)) { if (!mapDic[dataType].Contains(handlerType)) { mapDic[dataType].Add(handlerType); } } else { mapDic[dataType] = new List<Type>() { handlerType }; } } //取消注册只是取消EventBus中的事件处理,并不需要处理容器,所以代码相同 public void Unregister<TEventData>(Type handler) where TEventData : IEventData { var dataType = typeof(TEventData); Unregister(dataType, handler); } public void Unregister(Type eventType, Type handler) { if (mapDic.Keys.Contains(eventType)) { if (mapDic[eventType].Contains(handler)) { mapDic[eventType].Remove(handler); } } } /// <summary> /// 触发调用处理事件 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <param name="eventData"></param> public void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData { // var dataType = typeof(TEventData); var dataType = eventData.GetType(); var handlerTypes = mapDic[dataType]; foreach (var handlerType in handlerTypes) { //从Ioc容器中获取所有的实例 var handlerInterface = handlerType.GetInterface("IEventHandler`1"); var eventHandlers = IocContainer.ResolveAll(handlerInterface); //循环遍历,仅当解析的实例类型与映射字典中事件处理类型一致时,才触发事件 foreach (var eventHandler in eventHandlers) { if (eventHandler.GetType() == handlerType) { var handler = eventHandler as IEventHandler<TEventData>; handler?.Handle(eventData); } } } } } }
IOC(第二种)
using Castle.MicroKernel.Registration; using Castle.Windsor; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EventBus { public class IOCEventBus : IEventBus { private IOCEventBus() { IocContainer = new WindsorContainer(); mapDic = new ConcurrentDictionary<Type, List<Type>>(); } //EventBus单例模式 public static IOCEventBus Default = new IOCEventBus(); public IWindsorContainer IocContainer { get; private set; } private ConcurrentDictionary<Type, List<Type>> mapDic; public void Register<TEventData>(Type handlerType) where TEventData : IEventData { //将数据存储到mapDic var dataType = typeof(TEventData); Register(dataType, handlerType); } public void Register(Type dataType, Type handlerType) { //注册IEventHandler<T>到IOC容器 var handlerInterface = handlerType.GetInterface("IEventHandler`1"); if (!IocContainer.Kernel.HasComponent(handlerInterface)) {
IocContainer.Register(
Component.For(handlerInterface, handlerType));
} //放入总线中 if (mapDic.Keys.Contains(dataType)) { if (!mapDic[dataType].Contains(handlerType)) { mapDic[dataType].Add(handlerType); } } else { mapDic[dataType] = new List<Type>() { handlerType }; } } //取消注册只是取消EventBus中的事件处理,并不需要处理容器,所以代码相同 public void Unregister<TEventData>(Type handler) where TEventData : IEventData { var dataType = typeof(TEventData); Unregister(dataType, handler); } public void Unregister(Type eventType, Type handler) { if (mapDic.Keys.Contains(eventType)) { if (mapDic[eventType].Contains(handler)) { mapDic[eventType].Remove(handler); } } } /// <summary> /// 触发调用处理事件 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <param name="eventData"></param> public void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData { // var dataType = typeof(TEventData); var dataType = eventData.GetType(); var handlerTypes = mapDic[dataType]; foreach (var handlerType in handlerTypes) { var eventHandler = IocContainer.Resolve(handlerType); var handler = eventHandler as IEventHandler<TEventData>; handler.Handle(eventData); } } } }
上面的代码都是可以正常运行,其实上面的两种方式只是代码上书写的区别,主要是注入的方式不同,导致代码书写的方式不同
第一种方式Tigger的时候比多,效率比较低,第二种方式代码较少,同时效率较高,但是向容器注入的类型比较多。Component.For方法上的使用还是很有意思的,第二种种方式直接向For的方法中传入了接口和类两个参数,而这个方法的作用,就是为传入的类型创建注入实例的,所以会在容器中注入接口和类。所以在后面Trigger的时候可以直接通过类的类型直接获取到实例。For方法最终在源码中会调用AddService
上面的事件总线只是简单实现了基本的功能,基本上就是ABP事件总线的最最简单的模型,到此为止,事件总线的学习结束。
以上是关于ABP之事件总线的主要内容,如果未能解决你的问题,请参考以下文章
片段实例中带有 Otto 事件总线的 IllegalArgumentException
ABP Framework 5.3.0 版本新增功能和变更说明