如何指定Business Event和Command之间的关系?

既然是基于惯例优先原则,那么我们首先需要定义一个惯例:

1.调度事件和调度处理器之间是一对多关系(多对多的话,相信你看完了以后应该会知道怎么改的)。

2.所有业务事件(Event)要以调度事件为基类,业务指令(Command)的调度处理器特性需要指定可处理的调度事件。

  1. /// <summary>
  2. /// 调度事件
  3. /// </summary>
  4. public class DispatchEvent
  5. {
  6.  
  7. }
  1. /// <summary>
  2. /// 调度处理器
  3. /// </summary>
  4. [AttributeUsage(AttributeTargets.Method)]
  5. public class DispatchHandlerAttribute : Attribute
  6. {
  7. /// <summary>
  8. /// 观察的调度事件类型
  9. /// </summary>
  10. public Type ObservedDispatchEventType { get; set; }
  11.  
  12. /// <summary>
  13. /// 调度实例
  14. /// </summary>
  15. public object DispatchInstance { get; set; }
  16.  
  17. /// <summary>
  18. /// 动作方法信息
  19. /// </summary>
  20. public MethodInfo ActionMethodInfo { get; set; }
  21.  
  22. /// <summary>
  23. /// 构造函数
  24. /// </summary>
  25. /// <param name="observedDispatchEventType">观察的调度事件类型</param>
  26. public DispatchHandlerAttribute(Type observedDispatchEventType)
  27. {
  28. this.ObservedDispatchEventType = observedDispatchEventType;
  29. }
  30. }
如何创建一个业务事件?

我们基于获取AccessToken作为第一个业务事件,先创建一个获取AccessToken的事件

当然,具体交互微信细节就省略了,如果感兴趣可留言,看多少人关注我考虑下是否把微信相关的东西也一起加进来

  1. /// <summary>
  2. /// 获取微信交互接口凭证事件
  3. /// </summary>
  4. public class GetAccessTokenEvent : DispatchEvent
  5. {
  6. /// <summary>
  7. /// 微信交互接口凭证信息
  8. /// </summary>
  9. public AccessTokenInfo AccessTokenInfo { get; set; }
  10. }

此时有人会问了,不是获取AccessToken吗?传的不应该是一些APPID、APPSecurity之类的吗,为什么是AccessTokenInfo?
嗯,伟大的值类型和引用类型就派上用场了,因为Event会作为参数传递给Command,由Command自行填充。既然GetAccessTokenEvent是引用类型,那么在Command内修改AccessToken是不需要返回一个新的Event或者对象,直接在Event内的AccessTokenInfo上修改就好了,调用者就会得到他想要的东西。

虽然这只是个小知识点,大多数人都知道,但是有人喜欢用,因为可以偷懒。

有人不喜欢,觉得这样会让一些人不明白内部到底做了些什么,调用者该如何使用这个事件。

具体怎么做,因人而异吧,这不是重点,关键是一开始就提了:惯例优先原则。而这,不就是一个惯例吗? ^_^

如何创建一个业务指令,与上一个业务事件关联起来?

这里有个小小的业务,就是AccessToken获取后有失效时间,过了要重新获取。又不能每次都获取,因为又有获取次数限制。

为了处理这个问题,我做了个自动更新缓存类,这个在AccessTokenCommand的静态构造函数里设置一次获取逻辑。

之后其他功能在使用到以后都是从缓存里拿的。那么这个稍微有点逻辑的业务我们看看Command该怎么写。

  1. /// <summary>
  2. /// 微信交互接口凭证命令
  3. /// </summary>
  4. public class AccessTokenCommand
  5. {
  6. #region 静态构造函数
  7.  
  8. static AccessTokenCommand()
  9. {
  10. AutoUpdateCache.Add(CacheKeySet.AccessToken.ToString(), new AutoUpdateItem()
  11. {
  12. UpdateValue = (AutoUpdateItem autoUpdateItem) =>
  13. {
  14. var accessTokenInfo = CommandHelper.GetWeChatResponseObject<AccessTokenInfo>(new AccessTokenCommandRequest());
  15.  
  16. autoUpdateItem.ExpiredSeconds = accessTokenInfo.ExpiresIn - ;//预留过期时效,防止提前过期
  17. autoUpdateItem.Value = accessTokenInfo;
  18. }
  19. });
  20. }
  21.  
  22. #endregion
  23.  
  24. /// <summary>
  25. /// 获取微信交互接口凭证
  26. /// </summary>
  27. /// <param name="e"></param>
  28. [DispatchHandler(typeof(GetAccessTokenEvent))]
  29. public void GetAccessToken(GetAccessTokenEvent e)
  30. {
  31. e.AccessTokenInfo = AutoUpdateCache.GetValue<AccessTokenInfo>(CacheKeySet.AccessToken.ToString());
  32. }
  33. }

从 GetAccessToken 这个方法我们可以看出他有几个地方是需要注意的。
1.有一个特性 [DispatchHandler(typeof(GetAccessTokenEvent))] ,这个意思是标识当前方法是一个调度处理器,所处理的事件是 GetAccessTokenEvent

2.没有任何返回值

3.直接把AccessTokenInfo赋值到 GetAccessTokenEvent 这个传入参数里

如何把Event和Command关联起来呢?

我先声明,我本人是不太喜欢写一坨坨的配置文件的,虽然配置更灵活,但配置太多反而维护起来极度不方便。

那么,不使用配置就相当于要有一定的规则,否则我们是梳理不出来如何自动创建关联关系。我们就叫他“调度器”好了。

这个调度器应该具备以下几个功能:

1.解析任意一个程序集

2.扫描所有的DispatchHandler,即调度处理器

3.缓存调度关系网,降低反射带来的性能损耗

4.传一个调度事件参数,自行调用所有的调度处理器

  1. /// <summary>
  2. /// 调度器
  3. /// </summary>
  4. public class Dispatcher
  5. {
  6. /// <summary>
  7. /// 调度关系网
  8. /// </summary>
  9. private static Dictionary<Type, List<DispatchHandlerAttribute>> _dicDispatchRelativeNetwork = new Dictionary<Type, List<DispatchHandlerAttribute>>();
  10.  
  11. /// <summary>
  12. /// 建立调度关系网
  13. /// </summary>
  14. /// <param name="assembly"></param>
  15. public static void BuildDispatchRelationship(Assembly assembly)
  16. {
  17. Logger.Info("调度器:开始建立调度关系网...");
  18.  
  19. var types = assembly.GetTypes();
  20.  
  21. foreach (var type in types)
  22. {
  23. var methods = type.GetMethods();
  24.  
  25. foreach (var method in methods)
  26. {
  27. var attribute = method.GetCustomAttributes(typeof(DispatchHandlerAttribute), true).FirstOrDefault();
  28. if (attribute != null)
  29. {
  30. CheckParameterRule(method);
  31.  
  32. var handler = attribute as DispatchHandlerAttribute;
  33. handler.DispatchInstance = Activator.CreateInstance(type);
  34. handler.ActionMethodInfo = method;
  35. AddDispatchRelation(handler);
  36. }
  37. }
  38. }
  39. }
  40.  
  41. /// <summary>
  42. /// 添加调度关系
  43. /// </summary>
  44. /// <param name="handler">调度处理器</param>
  45. private static void AddDispatchRelation(DispatchHandlerAttribute handler)
  46. {
  47. var eventType = handler.ObservedDispatchEventType;
  48.  
  49. if (!_dicDispatchRelativeNetwork.ContainsKey(eventType))
  50. {
  51. _dicDispatchRelativeNetwork.Add(eventType, new List<DispatchHandler>());
  52. }
  53.  
  54. _dicDispatchRelativeNetwork[eventType].Add(handler);
  55.  
  56. Logger.Info(string.Format("调度器:建立新的关系网 [{0}]-[{1}.{2}]", eventType.Name, handler.DispatchInstance.GetType().Name, handler.ActionMethodInfo.Name));
  57. }
  58.  
  59. /// <summary>
  60. /// 检查参数规则
  61. /// </summary>
  62. /// <param name="method"></param>
  63. private static void CheckParameterRule(MethodInfo method)
  64. {
  65. var parameters = method.GetParameters();
  66. if (parameters == null ||
  67. parameters.Length != ||
  68. (!parameters.FirstOrDefault().ParameterType.Equals(typeof(DispatchEvent)) &&
  69. !parameters.FirstOrDefault().ParameterType.BaseType.Equals(typeof(DispatchEvent))
  70. ))
  71. {
  72. throw new Exception(string.Format("DispatchHandler - [{0}]的参数必须是只有一个且继承自DispatchEvent", method.Name));
  73. }
  74. }
  75.  
  76. /// <summary>
  77. /// 激活事件
  78. /// </summary>
  79. /// <param name="dispatchEvent">调度事件</param>
  80. public static void ActiveEvent(DispatchEvent dispatchEvent)
  81. {
  82. var type = dispatchEvent.GetType();
  83.  
  84. if (!_dicDispatchRelativeNetwork.ContainsKey(type))
  85. {
  86. Logger.Error(string.Format("调度器:当前事件[{0}]没有找到绑定的Handler", type.FullName));
  87. return;
  88. }
  89.  
  90. _dicDispatchRelativeNetwork[type].ForEach(action =>
  91. {
  92. ActiveAction(action, dispatchEvent);
  93. });
  94. }
  95.  
  96. private static void ActiveAction(DispatchHandlerAttribute action, DispatchEvent dispatchEvent)
  97. {
  98. try
  99. {
  100. action.ActionMethodInfo.Invoke(action.DispatchInstance, new object[] { dispatchEvent });
  101. }
  102. catch (Exception ex)
  103. {
  104. if (ex.InnerException != null && ex.InnerException.GetType().Equals(typeof(WCFLib.ExceptionExtension.GException)))
  105. {
  106. throw ex.InnerException;
  107. }
  108. else
  109. {
  110. throw;
  111. }
  112. }
  113. }
  114. }
如何激活Event?

创建一个事件,使用调度器激活事件,最后从事件中的属性获取你需要的返回值。

  1. var getAccessTokenEvent = new GetAccessTokenEvent();
  2. Dispatcher.ActiveEvent(getAccessTokenEvent);
  3. var accessTokenInfo = getAccessTokenEvent.AccessTokenInfo;

此时又有人会问了,为什么不直接用  new AccessTokenCommand().GetAccessToken(new GetAccessTokenEvent());
因为Event所在类库是没有添加Command引用的。通过调度者动态加载的。

那么问题又来了,这么劳民伤财为的是什么?

卖个关子,下篇继续造轮子。 ^_^

别急别急,还没完

这年头不应该有图有真相吗?

感谢所有耐心看完的人,欢迎提出你们的宝贵意见和批评。让我们一起进步吧。

下一篇预告:【轮子狂魔】调度器的扩展能力

如果你喜欢这篇博文,或者期待下一篇博文,麻烦点下推荐,你们的支持是我的动力 ^_^

【轮子狂魔】打造简易无配置的IoC的更多相关文章

  1. 【轮子狂魔】抛弃IIS,打造个性的Web Server - WebAPI/Lua/MVC(附带源码)

    引言 此篇是<[轮子狂魔]抛弃IIS,向天借个HttpListener - 基础篇(附带源码)>的续篇,也可以说是提高篇,如果你对HttpListener不甚了解的话,建议先看下基础篇. ...

  2. 树莓派最简易Wifi配置

    树莓派最简易Wifi配置 相信我,连博客都会偷懒写个最简易给你看 前提,只有一根网线没有网络的前提下进行的. 基于Win10系统和树莓派2015-05-05-raspbian-wheezy.img测试 ...

  3. 打造简易可扩展的jQuery/CSS3 Tab菜单

    原文:打造简易可扩展的jQuery/CSS3 Tab菜单 今天我们利用jQuery和CSS3来打造一款简易而且扩展性强的Tab菜单,这款Tab菜单在切换时也有滑块的效果,先来看看效果图: 由与Tab菜 ...

  4. 【轮子狂魔】手把手教你自造Redis Client

    为什么做Redis Client? Redis Client顾名思义,redis的客户端,主要是封装了一些对于Redis的操作. 而目前用的比较广泛的 ServiceStack.Redis 不学好,居 ...

  5. 移动工程后,打开ROM核无配置信息

    问题: 从他人处下载的ISE工程,打开dw51的ROM IP核,无配置信息,为block memory generator的初始配置,并显示无法找到coe文件 原因:ROM配置过程中的部分内容丢失导致 ...

  6. Spring-02 Java配置实现IOC

    Java配置 Spring4推荐使用java配置实现IOC Spring boot也推荐采用java配置实现IOC 在实际项目中,一般采用注解配置业务bean,全局配置使用Java配置. Java配置 ...

  7. hi-nginx-java的无配置路由配置

    hi-nginx-java既可以通过实现hi.servlet抽象来像Flask那样快速配置路由,例如: 1 hi.route r = hi.route.get_instance(); 2 r.get( ...

  8. Golang项目的配置管理——Viper简易入门配置

    Golang项目的配置管理--Viper简易入门配置 What is Viper? From:https://github.com/spf13/viper Viper is a complete co ...

  9. 撸了一个简易的配置中心,顺带整合到了SpringCloud

    大家好,我是三友~~ 最近突然心血来潮(就是闲的)就想着撸一个简单的配置中心,顺便也照葫芦画瓢给整合到SpringCloud. 本文大纲 配置中心的概述 随着历史的车轮不断的前进,技术不断的进步,单体 ...

随机推荐

  1. 【vue】饿了么项目-header组件开发

    1.数据传递的理解 在App.vue中用到了header组件,首先注册组件 components: { 'v-header': header } 然后才能引用 <v-header :seller ...

  2. pymongo的安装和使用

    1.安装 MongoDB的python接口pymongo的安装方法有多种,如源码.easy_install.pip都可以.采用pip安装,很简单. pip install pymongo 安装完成后可 ...

  3. PyDev For Eclipse

    eclipse安装Pydev 1.根据自己的eclipse和你安装的python选择可以安装的版本 Update sites for various PyDev versions: Latest ve ...

  4. 【luogu P1373 小a和uim之大逃离】 题解

    题目链接:https://www.luogu.org/problemnew/show/P1373 想不出来状态 看了一眼题解状态明白了 dp[i][j][h][1/0] 表示在i,j点差值为h是小A还 ...

  5. JS知识点整理(二)

    前言 这是对平时的一些读书笔记和理解进行整理的第二部分,第一部分请前往:JS知识点整理(一).本文包含一些易混淆.遗漏的知识点,也会配上一些例子,也许不是很完整,也许还会有点杂,但也许会有你需要的,后 ...

  6. 使用xampp发现php的date()函数与本地相差7个小时

    具体方法: 1. 打开php.ini 2. 搜索timezone 3. 修改为PRC 4. 回车键 5. 修改为PRC 6. 完成 没想到这么一个小问题也是一个大坑,在网上找了半天基本都是说要修改这个 ...

  7. 学习ThinkPHP5的第一天(安装 连接数据库)

    参考文档:thinkPHP5.0完全手册  一.安装 采用的是git安装方式: 应用项目:https://github.com/top-think/think 核心框架:https://github. ...

  8. microPython环境安装及使用

    1.ESP8266_12E(NodeMCU1.0)(AI Thinker)板Arduino IDE环境安装 (1)方法1(自动安装,windows,mac,linux平台都可) http://ardu ...

  9. 基于opencv和qt的人脸检测小系统

    摘要:利用opencv读取视频.图片并检测人脸,利用QT显示窗口,功能选择等 环境:Ubuntu18.04.OpenCV3.4.0.QT5.10.1 效果图: 代码如下(比较简单没什么注释): mai ...

  10. 一个sqoop export案例中踩到的坑

    案例分析: 需要将hdfs上的数据导出到mysql里的一张表里. 虚拟机集群的为:centos1-centos5 问题1: 在centos1上将hdfs上的数据导出到centos1上的mysql里: ...