百行代码打造一个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容器内部的构造原理是可以的,如果在项目中使用需要在健壮性等上面再稍加完善。