手写一个.net 依赖注入容器(1)--核心概念

该章节参考 asp.netcore框架揭秘(蒋金楠著)第3章。

该片文章记录一个自己定义的依赖注入容器,在这个过程中我们可以:

看到具体使用的那些技术,比如反射等
一个依赖注入容器需要包含的几个概念
用到的一些面向对象的设计模式和设计原则
废话不多说,我们开始吧。

控制反转

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。最常见的控制反转是依赖注入(Dependency Injection,简称DI)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

在http://ASP.NET中,控制反转体现在将应用对流程的控制转移到框架之中。对比在传统面向类库编程的时代,针对HTTP请求处理的流程被牢牢地控制在应用程序之中。引入框架之后,请求处理的控制权转移到了框架之中。

在好莱坞,演员把简历递交给电影公司后就只能回家等待消息。由于电影公司对整个娱乐项目具有完全控制权,演员只能被动地接受电影公司的邀约。“不要给我们打电话,我们会给你打电话”(Don’tcallus,we’llcallyou)——这就是著名的好莱坞法则(HollywoodPrinciple或者HollywoodLow),IoC完美地体现了这一法则:框架就好比是电影公司,掌握全局工作流程,引用程序就像是演员,它只需要按照框架指定的规则注册这些组件,框架会在将来的某一个时刻自动加载并执行注册的组件。

总的来说,在一个框架的基础上进行应用开发,就相当于在一条调试好的流水线上生产某种产品。只需要在相应的环节准备对应的原材料,最终下线的就是我们希望得到的产品。IoC几乎是所有框架均具有的一个固有属性,从这个意义上讲,IoC框架其实是一种错误的说法,可以说世界上本没有IoC框架,也可以说所有的框架都是IoC框架。

前置知识

我们创建的这个DI使用了大量关于泛型以及反射的知识,我们先把这部分知识放置到这里,熟悉之后,我们演示一个自定义的DI。

检查一个类型是否为泛型

 Type tt = typeof(IEnumerable<>);
 Type td = typeof(IEnumerable<int>);
 Console.WriteLine(td.IsGenericType);
 Console.WriteLine(tt.IsGenericType);

上述的输出都是true,不管IEnumerable<>有没有封闭。但是有的时候我们需要检查泛型类型是否封闭,这就需要用到另外的方法。

检查一个泛型类型是否封闭

  Type tt = typeof(IEnumerable<>);
   Type td = typeof(IEnumerable<int>);
   Console.WriteLine(tt.IsGenericTypeDefinition);
   Console.WriteLine(td.IsGenericTypeDefinition);

tt没有封闭,使用IsGenericTypeDefinition输出true。td则为false。关于IsGenericTypeDefinition的官方文档释义如下:Gets a value indicating whether the current Type represents a generic type definition, from which other generic types can be constructed(获取一个值,该值指示当前 Type 是否表示可以用来构造其他泛型类型的泛型类型定义。 也就是说表明 这个 type 是否可以用于构建泛型定义 )

另外还有一个ContainsGenericParameters,这个属性用于指示是否还有可以被替换掉的泛型参数:

Type tt = typeof(IEnumerable<>);
Type td = typeof(IDictionary<,>);
Console.WriteLine(tt.ContainsGenericParameters);//true
Console.WriteLine(td.ContainsGenericParameters);//true

封闭一个泛型类型

MakeGenericType可以让一个泛型类型封闭,前提是IsGenericTypeDefinition必须返回true,如果对一个已经封闭的泛型类型继续MakeGenericType,那么就会得到一个异常:

System.InvalidOperationException:“System.Collections.Generic.IDictionary`2[System.String,System.Int32] is not a GenericTypeDefinition. MakeGenericType may only be called on a type for which Type.IsGenericTypeDefinition is true.”

开始正题

设计一个依赖注入容器时我们需要考虑以下几个方面:

1、依赖注入容器本身,所有的服务以及服务的依赖均通过这个容器来提供。

2、表示一个服务注册细节的类,注册在这里是一个名词,包含服务的注册类型,服务的具体类型,以及如何创建这个服务的一个工厂。

3、服务注册时指定的生命周期,我们可以通过一个枚举来表示它

1、服务注册

定义一个ServiceRegistry来表示服务注册的概念

public class ServiceRegistry
    {
        public Type ServiceType { get; set; }
        public Lifetime Lifetime { get; set; }
        public Func<Cat, Type[], object> Factory { get; set; }

        public ServiceRegistry Next { get; set; }
        public ServiceRegistry(Type serviceType, Lifetime lifeTime, Func<Cat, Type[], object> factory)
        {
            ServiceType = serviceType;
            Lifetime = lifeTime;
            Factory = factory;
        }

        internal IEnumerable<ServiceRegistry> AsEnumerable()
        {
            var list = new List<ServiceRegistry>();
            for (var self = this; self != null; self = Next)
            {
                list.Add(self);
            }
            return list;
        }
    }

服务注册类ServiceRegistry有三个核心的属性,他们分别表示服务类型的ServiceType、服务生命周期的Lifetime以及用来创建服务实例的工厂Factory。

ServiceRegistry核心功能就是要表述清楚注册的服务类型、服务类型对应的服务实现以及服务的生命周期。这其中服务类型用ServiceType来表示,服务实现用Factory来表示。

最终用来创建服务实例的工厂体现为一个类型为Func<Cat,Type[],object>的委托对象,前两个输入分别代表当前使用的Cat对象和泛型类型参数(如果服务是泛型的话)。如果服务并不是一个泛型,那么这个参数会被指定为一个空数组。

此外,如果针对同一个服务类型(ServiceType)注册了多个服务实现时,ServiceRegistry可以形成一个链表结构来表达这种状态,Next保存了链表节点的下一个节点的引用。

我们还为ServiceRegistry定义了一个AsEnumerable方法,用于返回由当前节点和后续所有节点所表示的一个序列,如果当前节点是链表头,那么返回所用注册相同服务类型的服务实现。

2、服务的生命周期

不同的服务在整个应用程序存续期间的生命周期是不一样的,有的服务需要是单例的,处理全局的逻辑,有的服务是针对每次请求创建的,而有一些服务则是随用随取,用后即弃。这个概念在整个依赖注入的过程中非常重要,我们需要有一个类型来描述服务的生命周期:

 public enum Lifetime
    {
        Self,
        Root,
        Transient
    }

如上我们定义了三个枚举值,self类型的服务针对每次请求创建,处理完成后就销毁;root类型的服务处理全局逻辑,它一般能够存活到应用程序销毁;transient则是随用随取,用后即弃。

3、服务的容器

接着我们来看看服务容器的定义,我们将服务容器定义为一个Cat类,Cat这个命名很想象,它就像机器猫,大雄需要什么东西,都可以从机器猫那里要。

public class Cat : IServiceProvider, IDisposable
    {
        //作为根容器的Cat对象
        internal readonly Cat _root;
        //用来存储所有添加的服务注册
        internal readonly ConcurrentDictionary<Type, ServiceRegistry> _registries;
        //由当前Cat提供的非Transient生命周期的服务保存在_services上,Key是一个ServiceRegistry和泛型参数的组合
        internal readonly ConcurrentDictionary<Key, object> _services;
        //由于Cat需要完成将提供服务释放的工作,所以需要将实现了IDisposable的服务实例添加至_disposables中
        internal readonly ConcurrentBag<IDisposable> _disposables;
        private volatile bool _disposed;

        public Cat()
        {
            _root = this;
            _registries = new ConcurrentDictionary<Type, ServiceRegistry>();
            _services = new ConcurrentDictionary<Key, object>();
            _disposables = new ConcurrentBag<IDisposable>();

        }

        internal Cat(Cat parent)
        {
            _root = parent._root;
            _registries = parent._registries;
            _services = new ConcurrentDictionary<Key, object>();
            _disposables = new ConcurrentBag<IDisposable>();
        }

        public void Dispose()
        {
            _disposed = true;
            foreach (var disposable in _disposables)
            {
                disposable.Dispose();
            }
            _disposables.Clear();
            _services.Clear();
        }

        private void EnsureNotDisposed()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("Cat");
            }
        }

        public void Register(ServiceRegistry registry)
        {
            EnsureNotDisposed();
            if (_registries.TryGetValue(registry.ServiceType, out var existing))
            {
                _registries[registry.ServiceType] = registry;
                registry.Next = existing;
            }
            else
            {
                _registries[registry.ServiceType] = registry;
            }
        }

        private object GetServiceCore(ServiceRegistry registry, Type[] genericArguments)
        {
            var key = new Key(registry, genericArguments);
            var serviceType = registry.ServiceType;
            switch (registry.Lifetime)
            {
                case Lifetime.Self:
                    return GetOrCreate(_services, _disposables);
                case Lifetime.Root:
                    return GetOrCreate(_root._services, _root._disposables);
                default:
                    {
                        var service = registry.Factory(this, genericArguments);
                        if (service is IDisposable disposable)
                        {
                            _disposables.Add(disposable);
                        }
                        return service;
                    }
            }

            object GetOrCreate(ConcurrentDictionary<Key, object> services, ConcurrentBag<IDisposable> disposables)
            {
                if (services.TryGetValue(key, out var service))
                {
                    return service;
                }
                service = registry.Factory(this, genericArguments);
                services[key] = service;
                if (service is IDisposable disposable)
                {
                    disposables.Add(disposable);
                }
                return service;
            }
        }
        /// <summary>
        /// 实现IServiceProvider的接口
        /// </summary>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        public object GetService(Type serviceType)
        {
            EnsureNotDisposed();
            if (serviceType == typeof(Cat) || serviceType == typeof(IServiceProvider))
            {
                return this;
            }
            ServiceRegistry registry;
            //IEnumerable<T>
            if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                var elementType = serviceType.GetGenericArguments()[0];
                if (!_registries.TryGetValue(serviceType, out registry))
                {
                    return Array.CreateInstance(elementType, 0);
                }
                var registies = registry.AsEnumerable();
                var services = registies.Select(it => GetServiceCore(it, Type.EmptyTypes)).ToArray();
                Array array = Array.CreateInstance(elementType, services.Length);
                services.CopyTo(array, 0);
                return array;
            }
            //Generic
            if (serviceType.IsGenericType && _registries.ContainsKey(serviceType))
            {
                Type definition = serviceType.GetGenericTypeDefinition();
                return _registries.TryGetValue(definition, out registry)
                    ? GetServiceCore(registry, serviceType.GetGenericArguments())
                    : null;
            }
            //Normal
            return _registries.TryGetValue(serviceType, out registry)
                ? GetServiceCore(registry, new Type[0])
                : null;
        }
    }

Cat内部保存的一些状态属性都是采用线程安全的ConcurrentDictionary和ConcurrentBag来保存的。这样就能够让Cat在多线程操作时保持线程安全。

Cat类型同时实现了IServiceProvider和IDisposable接口,IServiceProvider定义了GetService方法用于为获取服务提供了统一的访问接口。IDisposable则用于服务消费完成时,提供统一的释放接口。

Cat类型定义了两个构造方法,公共的构造方法用于创建一个根容器,另一个内部构造函数用于创建作为子容器的Cat对象,指定的Cat对象将作为父容器。

Cat作为DI容器提供了两个核心的功能,包括注册服务和获取服务。我们来看看Cat的这两个方法定义:

1、Register方法

注册服务由Register来完成。我们可以添加不同的扩展方法来为不同形式的注册提供支持,但是核心的注册功能是定义在Cat内部的Register来完成的。

Register方法是对Cat类的_registries属性的操作。我们注册的所有服务最终都被转换成_registries上的一条记录。根据Cat类的两个构造方法可知,不管是根容器还是子容器,他们所引用的_registries都是同一个。

Register逻辑很简单,就是如果提供的ServiceType已经存在,那么就调整这个ServiceType对应的ServiceRegistry的Next属性,让这些相同ServiceType所对应的ServiceRegistry能够形成一个链表,如果提供的ServiceType不存在,则直接在字典中添加一个新的ServiceRegistry。

2、GetService方法

GetService方法是获取服务的核心逻辑。同样的,我们可以定义很多扩展方法来通过不同的方式获取所需的服务实例,但是这些方法最终都是通过调用核心的GetService来执行的。

获取服务时需要处理一些额外的逻辑,除了返回服务实现以外,如果该服务实例本身实现了IDisposable接口,则需要在服务实现被消费后能够合理的释放。

GetService方法依赖GetServiceCore方法。这两个方法关注的面也不同,GetService方法提供了外界访问的统一接口。目前GetService方法支持以下几种类型的服务:①IEnumerable,当我们针对同一个服务类型创建了多个服务实现的时候,通过IEnumerable可以取出所有的服务实例(T是某个服务类型);②泛型的服务类型;③普通的服务类型。这些获取过程均需通过GetServiceCore来获取。

GetServiceCore则是处理服务获取的核心逻辑,它有两个参数:ServiceRegistry用于获取服务类型、服务生命周期以及如何创建这个服务实例(通过Factory);泛型类型参数数组则在服务类型是泛型时用于封闭这个泛型的服务类型。GetServiceCore就是一个工厂方法,它通过Cat的_services属性来缓存之前获取过的服务实例,如果有,则直接从_services里面拿,如果没有,则通过ServiceRegistry的Factory来创建一个。最重要的是,不同的生命周期,处理的逻辑也不一样:如果是root类型的服务,则通过_root的_services和_disposables来管理;如果是self类型的服务,则通过当前容器的_services和_disposables来管理;而transient类型的服务则是直接通过registry的Factory来创建(不通过缓存),销毁的过程则和self的一样,也是通过当前的_disposables来管理的。

如果你对这些代码有一些疑惑,那么我可以告诉你们一个关键线索:Cat的_registries字段能够通过Type找到对应的ServiceRegistry,而后者是GetServiceCore需要的参数

Cat的_services字段能够缓存之前获取过的服务,提高了依赖注入的效率,它也是一个ConcurrentDictionary类型的字段,Key是我们自定义的一个类型:

public class Key : IEquatable<Key>
    {
        public ServiceRegistry Registry { get; }
        public Type[] GenericArguments { get; }
        public Key(ServiceRegistry registry, Type[] genericArguments)
        {
            Registry = registry;
            GenericArguments = genericArguments;
        }

        public bool Equals(Key other)
        {
            if (Registry != other.Registry) return false;
            if (GenericArguments.Length != other.GenericArguments.Length) return false;
            for (int i = 0; i < GenericArguments.Length; i++)
            {
                if (GenericArguments[i].GetHashCode() != other.GenericArguments[i].GetHashCode()) return false;
            }
            return true;
        }

        public override int GetHashCode()
        {
            var hashCode = Registry.GetHashCode();
            for (int i = 0; i < GenericArguments.Length; i++)
            {
                hashCode ^= GenericArguments[i].GetHashCode();
            }
            return hashCode;
        }

        public override bool Equals(object obj)
        {
            return obj is Key key ? Equals(key) : false;
        }
    }

此外,在Cat实现的Dispose方法中,将当前Cat实例的_disposables字段中保存的成员全部执行一遍Dispose,然后将它和_services字段中的成员全部清除。

至此,我们的有关依赖注入的核心部件就已经介绍完毕。现在系统和依赖注入有直接关系的实际就三个类:一个表示服务注册的ServiceRegistry;一个表示服务容器的Cat;一个表示服务生命周期的Lifetime。

之后,就是为Cat定义一系列的扩展方法,来让服务的注册和获取更加灵活。

4、扩展方法

基于Cat的扩展方法有两类,一类是用于注册,另一类用于获取服务。

注册的核心就是生成一个ServiceRegistry,而ServiceRegistry的核心就是它所负责的工厂Factory,还记得上面我们说的这个Factory吗?它是一个Func<Cat,Type[],object>类型的委托。所以,下面我们要做的关于注册的扩展方法,都是围绕如何生成一个Factory来进行的。

 public static class CatExtensions
    {
        public static Cat Register(this Cat cat, Type from, Type to, Lifetime lifetime)
        {
            Func<Cat, Type[], object> factory = (_, argument) => Create(_, to, argument);
            cat.Register(new ServiceRegistry(from, lifetime, factory));
            return cat;
        }


        public static Cat Register<TFrom, TTo>(this Cat cat, Lifetime lifetime) where TTo : TFrom
        {
            return cat.Register(typeof(TFrom), typeof(TTo), lifetime);
        }

        public static Cat Register(this Cat cat, Type serviceType, object instance)
        {
            Func<Cat, Type[], object> factory = (_, arguments) => instance;
            cat.Register(new ServiceRegistry(serviceType, Lifetime.Root, factory));
            return cat;
        }

        public static Cat Register<TService>(this Cat cat, TService instance)
        {
            Func<Cat, Type[], object> factory = (_, arguments) => instance;
            cat.Register(new ServiceRegistry(typeof(TService), Lifetime.Root, factory));
            return cat;
        }

        public static Cat Register(this Cat cat, Type serviceType, Func<Cat, object> factory, Lifetime lifetime)
        {
            cat.Register(new ServiceRegistry(serviceType, lifetime, (_, argument) => factory(_)));
            return cat;
        }

        public static Cat Regsiter<TService>(this Cat cat, Func<Cat, TService> factory, Lifetime lifetime)
        {
            cat.Register(new ServiceRegistry(typeof(TService), lifetime, (_, arguments) => factory(_)));
            return cat;
        }

        public static Cat CreateChild(this Cat cat)
        {
            return new Cat(cat);//调用internal的构造函数
        }

        private static object Create(Cat cat, Type type, Type[] genericArguments)
        {
            if (genericArguments.Length > 0)
            {
                type = type.MakeGenericType(genericArguments);
            }

            var constructors = type.GetConstructors();
            if (constructors.Length == 0)
            {
                throw new InvalidOperationException($"canot create a instance of {type} which does not hava a public constructor");
            }
            var constructor = constructors.FirstOrDefault(it => it.GetCustomAttributes(false).OfType<InjectionAttribute>().Any());
            constructor ??= constructors.First();
            ParameterInfo[] parameters = constructor.GetParameters();
            if (parameters.Length == 0)
            {
                return Activator.CreateInstance(type);
            }
            var arguments = new object[parameters.Length];
            for (int index = 0; index < arguments.Length; index++)
            {
                arguments[index] = cat.GetService(parameters[index].ParameterType);
            }
            return constructor.Invoke(arguments);
        }
    }

转载于:
https://zhuanlan.zhihu.com/p/442113796

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值