首页 > 代码库 > 【轮子狂魔】打造简易无配置的IoC

【轮子狂魔】打造简易无配置的IoC

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

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

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

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

1     /// <summary>2     /// 调度事件3     /// </summary>4     [AttributeUsage(AttributeTargets.Method)]5     public class DispatchEvent : Attribute6     {7 8     }

 

 1     /// <summary> 2     /// 调度处理器 3     /// </summary> 4     [AttributeUsage(AttributeTargets.Method)] 5     public class DispatchHandler : 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 DispatchHandler(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该怎么写。

    /// <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 - 30;//预留过期时效,防止提前过期                    autoUpdateItem.Value =http://www.mamicode.com/ 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.传一个调度事件参数,自行调用所有的调度处理器

  1     /// <summary>  2     /// 调度器  3     /// </summary>  4     public class Dispatcher  5     {  6         /// <summary>  7         /// 调度关系网  8         /// </summary>  9         private static Dictionary<Type, List<DispatchHandler>> _dicDispatchRelativeNetwork = new Dictionary<Type, List<DispatchHandler>>(); 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(DispatchHandler), true).FirstOrDefault(); 28                     if (attribute != null) 29                     { 30                         CheckParameterRule(method); 31  32                         var handler = attribute as DispatchHandler; 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(DispatchHandler 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 != 1 || 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(DispatchHandler 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                 else109                 {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