【轮子狂魔】打造简易无配置的IoC
如何指定Business Event和Command之间的关系? |
既然是基于惯例优先原则,那么我们首先需要定义一个惯例:
1.调度事件和调度处理器之间是一对多关系(多对多的话,相信你看完了以后应该会知道怎么改的)。
2.所有业务事件(Event)要以调度事件为基类,业务指令(Command)的调度处理器特性需要指定可处理的调度事件。
- /// <summary>
- /// 调度事件
- /// </summary>
- public class DispatchEvent
- {
- }
- /// <summary>
- /// 调度处理器
- /// </summary>
- [AttributeUsage(AttributeTargets.Method)]
- public class DispatchHandlerAttribute : Attribute
- {
- /// <summary>
- /// 观察的调度事件类型
- /// </summary>
- public Type ObservedDispatchEventType { get; set; }
- /// <summary>
- /// 调度实例
- /// </summary>
- public object DispatchInstance { get; set; }
- /// <summary>
- /// 动作方法信息
- /// </summary>
- public MethodInfo ActionMethodInfo { get; set; }
- /// <summary>
- /// 构造函数
- /// </summary>
- /// <param name="observedDispatchEventType">观察的调度事件类型</param>
- public DispatchHandlerAttribute(Type observedDispatchEventType)
- {
- this.ObservedDispatchEventType = observedDispatchEventType;
- }
- }
如何创建一个业务事件? |
我们基于获取AccessToken作为第一个业务事件,先创建一个获取AccessToken的事件
当然,具体交互微信细节就省略了,如果感兴趣可留言,看多少人关注我考虑下是否把微信相关的东西也一起加进来
- /// <summary>
- /// 获取微信交互接口凭证事件
- /// </summary>
- public class GetAccessTokenEvent : DispatchEvent
- {
- /// <summary>
- /// 微信交互接口凭证信息
- /// </summary>
- public AccessTokenInfo AccessTokenInfo { get; set; }
- }
此时有人会问了,不是获取AccessToken吗?传的不应该是一些APPID、APPSecurity之类的吗,为什么是AccessTokenInfo?
嗯,伟大的值类型和引用类型就派上用场了,因为Event会作为参数传递给Command,由Command自行填充。既然GetAccessTokenEvent是引用类型,那么在Command内修改AccessToken是不需要返回一个新的Event或者对象,直接在Event内的AccessTokenInfo上修改就好了,调用者就会得到他想要的东西。
虽然这只是个小知识点,大多数人都知道,但是有人喜欢用,因为可以偷懒。
有人不喜欢,觉得这样会让一些人不明白内部到底做了些什么,调用者该如何使用这个事件。
具体怎么做,因人而异吧,这不是重点,关键是一开始就提了:惯例优先原则。而这,不就是一个惯例吗? ^_^
如何创建一个业务指令,与上一个业务事件关联起来? |
这里有个小小的业务,就是AccessToken获取后有失效时间,过了要重新获取。又不能每次都获取,因为又有获取次数限制。
为了处理这个问题,我做了个自动更新缓存类,这个在AccessTokenCommand的静态构造函数里设置一次获取逻辑。
之后其他功能在使用到以后都是从缓存里拿的。那么这个稍微有点逻辑的业务我们看看Command该怎么写。
- /// <summary>
- /// 微信交互接口凭证命令
- /// </summary>
- public class AccessTokenCommand
- {
- #region 静态构造函数
- static AccessTokenCommand()
- {
- AutoUpdateCache.Add(CacheKeySet.AccessToken.ToString(), new AutoUpdateItem()
- {
- UpdateValue = (AutoUpdateItem autoUpdateItem) =>
- {
- var accessTokenInfo = CommandHelper.GetWeChatResponseObject<AccessTokenInfo>(new AccessTokenCommandRequest());
- autoUpdateItem.ExpiredSeconds = accessTokenInfo.ExpiresIn - ;//预留过期时效,防止提前过期
- autoUpdateItem.Value = accessTokenInfo;
- }
- });
- }
- #endregion
- /// <summary>
- /// 获取微信交互接口凭证
- /// </summary>
- /// <param name="e"></param>
- [DispatchHandler(typeof(GetAccessTokenEvent))]
- public void GetAccessToken(GetAccessTokenEvent e)
- {
- e.AccessTokenInfo = AutoUpdateCache.GetValue<AccessTokenInfo>(CacheKeySet.AccessToken.ToString());
- }
- }
从 GetAccessToken 这个方法我们可以看出他有几个地方是需要注意的。
1.有一个特性 [DispatchHandler(typeof(GetAccessTokenEvent))] ,这个意思是标识当前方法是一个调度处理器,所处理的事件是 GetAccessTokenEvent
2.没有任何返回值
3.直接把AccessTokenInfo赋值到 GetAccessTokenEvent 这个传入参数里
如何把Event和Command关联起来呢? |
我先声明,我本人是不太喜欢写一坨坨的配置文件的,虽然配置更灵活,但配置太多反而维护起来极度不方便。
那么,不使用配置就相当于要有一定的规则,否则我们是梳理不出来如何自动创建关联关系。我们就叫他“调度器”好了。
这个调度器应该具备以下几个功能:
1.解析任意一个程序集
2.扫描所有的DispatchHandler,即调度处理器
3.缓存调度关系网,降低反射带来的性能损耗
4.传一个调度事件参数,自行调用所有的调度处理器
- /// <summary>
- /// 调度器
- /// </summary>
- public class Dispatcher
- {
- /// <summary>
- /// 调度关系网
- /// </summary>
- private static Dictionary<Type, List<DispatchHandlerAttribute>> _dicDispatchRelativeNetwork = new Dictionary<Type, List<DispatchHandlerAttribute>>();
- /// <summary>
- /// 建立调度关系网
- /// </summary>
- /// <param name="assembly"></param>
- public static void BuildDispatchRelationship(Assembly assembly)
- {
- Logger.Info("调度器:开始建立调度关系网...");
- var types = assembly.GetTypes();
- foreach (var type in types)
- {
- var methods = type.GetMethods();
- foreach (var method in methods)
- {
- var attribute = method.GetCustomAttributes(typeof(DispatchHandlerAttribute), true).FirstOrDefault();
- if (attribute != null)
- {
- CheckParameterRule(method);
- var handler = attribute as DispatchHandlerAttribute;
- handler.DispatchInstance = Activator.CreateInstance(type);
- handler.ActionMethodInfo = method;
- AddDispatchRelation(handler);
- }
- }
- }
- }
- /// <summary>
- /// 添加调度关系
- /// </summary>
- /// <param name="handler">调度处理器</param>
- private static void AddDispatchRelation(DispatchHandlerAttribute handler)
- {
- var eventType = handler.ObservedDispatchEventType;
- if (!_dicDispatchRelativeNetwork.ContainsKey(eventType))
- {
- _dicDispatchRelativeNetwork.Add(eventType, new List<DispatchHandler>());
- }
- _dicDispatchRelativeNetwork[eventType].Add(handler);
- Logger.Info(string.Format("调度器:建立新的关系网 [{0}]-[{1}.{2}]", eventType.Name, handler.DispatchInstance.GetType().Name, handler.ActionMethodInfo.Name));
- }
- /// <summary>
- /// 检查参数规则
- /// </summary>
- /// <param name="method"></param>
- private static void CheckParameterRule(MethodInfo method)
- {
- var parameters = method.GetParameters();
- if (parameters == null ||
- parameters.Length != ||
- (!parameters.FirstOrDefault().ParameterType.Equals(typeof(DispatchEvent)) &&
- !parameters.FirstOrDefault().ParameterType.BaseType.Equals(typeof(DispatchEvent))
- ))
- {
- throw new Exception(string.Format("DispatchHandler - [{0}]的参数必须是只有一个且继承自DispatchEvent", method.Name));
- }
- }
- /// <summary>
- /// 激活事件
- /// </summary>
- /// <param name="dispatchEvent">调度事件</param>
- public static void ActiveEvent(DispatchEvent dispatchEvent)
- {
- var type = dispatchEvent.GetType();
- if (!_dicDispatchRelativeNetwork.ContainsKey(type))
- {
- Logger.Error(string.Format("调度器:当前事件[{0}]没有找到绑定的Handler", type.FullName));
- return;
- }
- _dicDispatchRelativeNetwork[type].ForEach(action =>
- {
- ActiveAction(action, dispatchEvent);
- });
- }
- private static void ActiveAction(DispatchHandlerAttribute action, DispatchEvent dispatchEvent)
- {
- try
- {
- action.ActionMethodInfo.Invoke(action.DispatchInstance, new object[] { dispatchEvent });
- }
- catch (Exception ex)
- {
- if (ex.InnerException != null && ex.InnerException.GetType().Equals(typeof(WCFLib.ExceptionExtension.GException)))
- {
- throw ex.InnerException;
- }
- else
- {
- throw;
- }
- }
- }
- }
如何激活Event? |
创建一个事件,使用调度器激活事件,最后从事件中的属性获取你需要的返回值。
- var getAccessTokenEvent = new GetAccessTokenEvent();
- Dispatcher.ActiveEvent(getAccessTokenEvent);
- var accessTokenInfo = getAccessTokenEvent.AccessTokenInfo;
此时又有人会问了,为什么不直接用 new AccessTokenCommand().GetAccessToken(new GetAccessTokenEvent());
因为Event所在类库是没有添加Command引用的。通过调度者动态加载的。
那么问题又来了,这么劳民伤财为的是什么?
卖个关子,下篇继续造轮子。 ^_^
别急别急,还没完
这年头不应该有图有真相吗? |
感谢所有耐心看完的人,欢迎提出你们的宝贵意见和批评。让我们一起进步吧。
下一篇预告:【轮子狂魔】调度器的扩展能力
如果你喜欢这篇博文,或者期待下一篇博文,麻烦点下推荐,你们的支持是我的动力 ^_^
【轮子狂魔】打造简易无配置的IoC的更多相关文章
- 【轮子狂魔】抛弃IIS,打造个性的Web Server - WebAPI/Lua/MVC(附带源码)
引言 此篇是<[轮子狂魔]抛弃IIS,向天借个HttpListener - 基础篇(附带源码)>的续篇,也可以说是提高篇,如果你对HttpListener不甚了解的话,建议先看下基础篇. ...
- 树莓派最简易Wifi配置
树莓派最简易Wifi配置 相信我,连博客都会偷懒写个最简易给你看 前提,只有一根网线没有网络的前提下进行的. 基于Win10系统和树莓派2015-05-05-raspbian-wheezy.img测试 ...
- 打造简易可扩展的jQuery/CSS3 Tab菜单
原文:打造简易可扩展的jQuery/CSS3 Tab菜单 今天我们利用jQuery和CSS3来打造一款简易而且扩展性强的Tab菜单,这款Tab菜单在切换时也有滑块的效果,先来看看效果图: 由与Tab菜 ...
- 【轮子狂魔】手把手教你自造Redis Client
为什么做Redis Client? Redis Client顾名思义,redis的客户端,主要是封装了一些对于Redis的操作. 而目前用的比较广泛的 ServiceStack.Redis 不学好,居 ...
- 移动工程后,打开ROM核无配置信息
问题: 从他人处下载的ISE工程,打开dw51的ROM IP核,无配置信息,为block memory generator的初始配置,并显示无法找到coe文件 原因:ROM配置过程中的部分内容丢失导致 ...
- Spring-02 Java配置实现IOC
Java配置 Spring4推荐使用java配置实现IOC Spring boot也推荐采用java配置实现IOC 在实际项目中,一般采用注解配置业务bean,全局配置使用Java配置. Java配置 ...
- hi-nginx-java的无配置路由配置
hi-nginx-java既可以通过实现hi.servlet抽象来像Flask那样快速配置路由,例如: 1 hi.route r = hi.route.get_instance(); 2 r.get( ...
- Golang项目的配置管理——Viper简易入门配置
Golang项目的配置管理--Viper简易入门配置 What is Viper? From:https://github.com/spf13/viper Viper is a complete co ...
- 撸了一个简易的配置中心,顺带整合到了SpringCloud
大家好,我是三友~~ 最近突然心血来潮(就是闲的)就想着撸一个简单的配置中心,顺便也照葫芦画瓢给整合到SpringCloud. 本文大纲 配置中心的概述 随着历史的车轮不断的前进,技术不断的进步,单体 ...
随机推荐
- 【vue】饿了么项目-header组件开发
1.数据传递的理解 在App.vue中用到了header组件,首先注册组件 components: { 'v-header': header } 然后才能引用 <v-header :seller ...
- pymongo的安装和使用
1.安装 MongoDB的python接口pymongo的安装方法有多种,如源码.easy_install.pip都可以.采用pip安装,很简单. pip install pymongo 安装完成后可 ...
- PyDev For Eclipse
eclipse安装Pydev 1.根据自己的eclipse和你安装的python选择可以安装的版本 Update sites for various PyDev versions: Latest ve ...
- 【luogu P1373 小a和uim之大逃离】 题解
题目链接:https://www.luogu.org/problemnew/show/P1373 想不出来状态 看了一眼题解状态明白了 dp[i][j][h][1/0] 表示在i,j点差值为h是小A还 ...
- JS知识点整理(二)
前言 这是对平时的一些读书笔记和理解进行整理的第二部分,第一部分请前往:JS知识点整理(一).本文包含一些易混淆.遗漏的知识点,也会配上一些例子,也许不是很完整,也许还会有点杂,但也许会有你需要的,后 ...
- 使用xampp发现php的date()函数与本地相差7个小时
具体方法: 1. 打开php.ini 2. 搜索timezone 3. 修改为PRC 4. 回车键 5. 修改为PRC 6. 完成 没想到这么一个小问题也是一个大坑,在网上找了半天基本都是说要修改这个 ...
- 学习ThinkPHP5的第一天(安装 连接数据库)
参考文档:thinkPHP5.0完全手册 一.安装 采用的是git安装方式: 应用项目:https://github.com/top-think/think 核心框架:https://github. ...
- microPython环境安装及使用
1.ESP8266_12E(NodeMCU1.0)(AI Thinker)板Arduino IDE环境安装 (1)方法1(自动安装,windows,mac,linux平台都可) http://ardu ...
- 基于opencv和qt的人脸检测小系统
摘要:利用opencv读取视频.图片并检测人脸,利用QT显示窗口,功能选择等 环境:Ubuntu18.04.OpenCV3.4.0.QT5.10.1 效果图: 代码如下(比较简单没什么注释): mai ...
- 一个sqoop export案例中踩到的坑
案例分析: 需要将hdfs上的数据导出到mysql里的一张表里. 虚拟机集群的为:centos1-centos5 问题1: 在centos1上将hdfs上的数据导出到centos1上的mysql里: ...