zoukankan      html  css  js  c++  java
  • 百行代码打造一个DI容器(支持瞬时生命周期、单利生命周期、构造函数自动注入、属性自动注入、字段自动注入)

    DI注入在.Net平台是非常流行的, 很多项目都用到了,很多开发人员或多或少也用到DI容器了,感觉DI容器很神奇很厉害。本文将通过百行代码展示DI容器的内部核心代码(包括组件的瞬时生命周期、单利生命周期、构造函数自动注入、属性自动注入、字段自动注入),揭开DI容器的神秘面纱。

       一、定义DI容器接口

    1:  public interface IContainer
    2: 
    {
    3:      void Register<TService, TComponent>(bool isSingleton = false);//组件注册
    4:     
    object Resolve(Type type);//解析组件
    5: 
    }

     
    二、定义组件元数据类
    1: class ComponentInfo
    2:  {
    3:      public Type Contact;//组件契约
    4:      public Type Implimention;//组件实现类型
    5:      public bool IsSingleton;//是否单利
    6:  }
     
    三、组件注册代码 
    01:  //组件元数据
    02:    Dictionary<Type, ComponentInfo> Components = new Dictionary<Type, ComponentInfo>();
    03:    public void Register<TService, TComponent>(bool isSingleton = false)
    04:    {
    05:        var serviceType = typeof(TService);
    06:        lock (Components)
    07:        {
    08:            Components[serviceType] = new ComponentInfo
    09:            { 
    10:                Contact = serviceType
    11:                , Implimention = typeof(TComponent)
    12:                , IsSingleton = isSingleton
    13:            };
    14:        }
    15:    }
     

    四、组件解析代码

    01:  public object Resolve(Type serviceType)
    02:   {
    03:       //
    04:       if (serviceType == null)
    05:           throw new ArgumentNullException("type");
    06:  
    07:       ComponentInfo t;
    08:       if (!Components.TryGetValue(serviceType, out t))
    09:           throw new ApplicationException(string.Format("{0} 没有配置到容器中", serviceType.FullName));
    10:  
    11:       //如果是单利类型,并且单利容器中存在则直接返回
    12:       if (t.IsSingleton && Instances.ContainsKey(serviceType))
    13:           return Instances[serviceType];
    14:  
    15:       //得到构造函数
    16:       var ctor = t.Implimention
    17:           .GetConstructors()
    18:           .OrderByDescending(c => c.GetParameters().Count())//按照构造函数参数的个数进行降序排列
    19:           .Where(c => c.GetParameters().TrueForAll(p =>
    20:                       Components.ContainsKey(p.ParameterType)))//构造函数的参数类型都必须已经被DI容器所注册
    21:           .FirstOrDefault();
    22:  
    23:       //解析构造函数的参数对象
    24:       var args = ctor
    25:           .GetParameters()
    26:           .Select(p => Resolve(p.ParameterType))
    27:           .ToArray();
    28:       
    29:       //创建对象
    30:       var o = ctor.Invoke(args);//构造函数参数注入
    31:       //如果组件是单利的,那么就保存到单利容器中
    32:       if (t.IsSingleton)
    33:           Instances.Add(serviceType, o);
    34:  
    35:       //属性注入
    36:       o.GetType()
    37:           .GetProperties()
    38:           .Where(p => Components.ContainsKey(p.PropertyType))
    39:           .ForEach(p => p.SetValue(o, Resolve(p.PropertyType), null));
    40:  
    41:       //字段注入 
    42:       o.GetType()
    43:           .GetFields()
    44:           .Where(p => Components.ContainsKey(p.FieldType))
    45:           .ForEach(p => p.SetValue(o, Resolve(p.FieldType)));
    46:  
    47:       return o;
    48:   }
     

    五、补充集合的两个扩展方法

    01:  internal static void ForEach<T>(this IEnumerable<T> coll, Action<T> handler)
    02:         {
    03:             foreach (var item in coll)
    04:                 handler(item);
    05:         }
    06:  
    07:         internal static bool TrueForAll<T>(this IEnumerable<T> coll, Func<T, bool> handler)
    08:         {
    09:             foreach (var item in coll)
    10:                 if (!handler(item))
    11:                     return false;
    12:             return true;
    13:         }

    总结:到此完成了DI容器的核心代码,对象的创建、字段的注入、属性的注入都是通过反射来完成的,性能有待优化,另外该容器的扩展性不足,也不支持GetAll等方法,但是做为研究和学习DI容器内部的构造原理是可以的,如果在项目中使用需要在健壮性等上面再稍加完善。

  • 相关阅读:
    Android : iperf-2.0.4 网络测试工具
    Android : SELinux 简析&修改
    Android : 修改内核源码 and 编译、打包成新的boot.img
    Bluedroid: 音频数据的传输流程
    Bluedroid: 蓝牙协议栈源码剖析
    【Java】 剑指offer(54) 二叉搜索树的第k个结点
    【Java】 剑指offer(53-3) 数组中数值和下标相等的元素
    【Java】 剑指offer(53-2) 0到n-1中缺失的数字
    【Java】 剑指offer(53-1) 数字在排序数组中出现的次数
    【Java】 剑指offer(52) 两个链表的第一个公共结点
  • 原文地址:https://www.cnblogs.com/netcasewqs/p/2589830.html
Copyright © 2011-2022 走看看