zoukankan      html  css  js  c++  java
  • 【轮子狂魔】打造简易无配置的IoC

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

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

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

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

    1     /// <summary>
    2     /// 调度事件
    3     /// </summary>
    4     public class DispatchEvent
    5     {
    6 
    7     }
    View Code
     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     }
    View Code
    如何创建一个业务事件?

     我们基于获取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     }
    View Code

    此时有人会问了,不是获取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 - 30;//预留过期时效,防止提前过期
    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     }
    View Code

    从 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 != 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(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     }
    View Code
    如何激活Event?

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

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

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

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

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

    别急别急,还没完

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

     

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

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

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

  • 相关阅读:
    升级Node和Calico
    K8S在原有的集群上新增node节点(v1.19.5)
    Django的quickstart
    Jenkins配置python自动化点检项目
    Django的ORM一对多查询及联合查询
    Django的ORM查询
    mac系统安装mysqlclient的一些坑(附解决方法)
    tomcat中Context标签使用
    pipline语法
    android----AndroidViewModel访问SharedPreference,重启手机数据也存在
  • 原文地址:https://www.cnblogs.com/doddgu/p/lunzikuangmo_simpleioc.html
Copyright © 2011-2022 走看看