zoukankan      html  css  js  c++  java
  • Core源码(八)依赖注入

    依赖注入的源码是Microsoft.Extensions.DependencyInjection命名空间下的,项目结构比较复杂,本文先从先从简单的实现开始,一起了解下依赖注入最基础的实现

    最基础的依赖注入

    依赖注入容器

    public class Cat
    {
        /// <summary>
        /// 线程安全的集合去保存所有的类型
        /// </summary>
        private ConcurrentDictionary<Type, Type> typeMapping = new ConcurrentDictionary<Type, Type>();
        //注册到容器中
        public void Register(Type from, Type to)
        {
            typeMapping[from] = to;
        }
    
        //这个是核心方法
        public object GetService(Type serviceType)
        {
            #region 构造函数注入
    
            Type type;
            if (!typeMapping.TryGetValue(serviceType, out type))
            {
                type = serviceType;
            }
            if (type.IsInterface || type.IsAbstract)
            {
                return null;
            }
            //获取构造函数的基本信息
            ConstructorInfo constructor = this.GetConstructor(type);
            if (null == constructor)
            {
                return null;
            }
            //这里递归的获取了所有需要的实例
            object[] arguments = constructor.GetParameters().Select(p => GetService(p.ParameterType)).ToArray();
            //根据获得的参数调用构造函数 得到所需对象service
            object service = constructor.Invoke(arguments);
            
            #endregion
    
            return service;
        }
    
        /// <summary>
        /// 获取对应类型的构造器
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        protected virtual ConstructorInfo GetConstructor(Type type)
        {
            ConstructorInfo[] constructors = type.GetConstructors();
            //如果我们有标记了InjectionAttribute 那么就用标记了的构造器
            return constructors.FirstOrDefault(c => c.GetCustomAttribute<InjectionAttribute>() != null)
                ?? constructors.FirstOrDefault();
        }
    }

    扩展方法

    public static class CatExtensions
    {
        public static T GetService<T>(this Cat cat)
        {
            if (cat == null)
            {
                throw new ArgumentNullException("collection");
            }
            return (T)cat.GetService(typeof(T));
        }
    
        public static void Register<TService, TImplementation>(this Cat cat)
        {
            if (cat == null)
            {
                throw new ArgumentNullException("collection");
            }
            cat.Register(typeof(TService), typeof(TImplementation));
        }
    }

    实际使用

    public interface IFoo { }
    public interface IBar { }
    public class Bar : IBar { }
    public class Foo : IFoo
    {
        public IBar Bar { get; private set; }
        public Foo() { }
        [Injection]
        public Foo(IBar bar)
        {
            this.Bar = bar;
        }
    }
    main方法
    Cat cat = new Cat();
    cat.Register<IFoo, Foo>();
    cat.Register<IBar, Bar>();
    
    IFoo service = cat.GetService<IFoo>();
    Foo foo = (Foo)service;
    Console.WriteLine("cat.GetService<IFoo>(): {0}", service);
    Console.WriteLine("cat.GetService<IFoo>().Bar: {0}", foo.Bar);

    这里我们通过public class InjectionAttribute : Attribute { } 标记需要注入的构造函数,然后我们简单的容器就会根据特性去选择对应执行的构造方法。

    带有实例生命周期管理的依赖注入

    ServiceRegistry

    使用ServiceRegistry类进行服务注册的基础单元,我们将针对同一个服务类型(ServiceType属性相同)的多个ServiceRegistry组成一个链表,作为相邻节点的两个ServiceRegistry对象通过Next属性关联起来。我们为ServiceRegistry定义了一个AsEnumerable方法是它返回由当前以及后续节点组成的ServiceRegistry集合。

    public enum Lifetime
    {
        Singlelton,
        Self,
        Transient
    }
    public class ServiceRegistry
    {
        public Type ServiceType { get; }
        public Lifetime Lifetime { get; }
        /// <summary>
        /// core的依赖注入还有实例模式,这里只有工厂模式做展示
        /// </summary>
        public Func<Cat, Type[], object> Factory { get; }
        /// <summary>
        /// 同一个服务类型(ServiceType属性相同)的多个ServiceRegistry组成一个链表
        /// </summary>
        internal ServiceRegistry Next { get; set; }
    
        public ServiceRegistry(Type serviceType, Lifetime lifetime, Func<Cat, Type[], object> factory)
        {
            ServiceType = serviceType;
            Lifetime = lifetime;
            Factory = factory;
        }
    
        public IEnumerable<ServiceRegistry> AsEnumerable()
        {
            var list = new List<ServiceRegistry>();
            for (var self = this; self != null; self = self.Next)
            {
                list.Add(self);
            }
            return list;
        }
    }
    ServiceRegistry

    主容器Cat

    public class Cat : IServiceProvider, IDisposable
    {
        internal Cat _root;
        /// <summary>
        /// 类型注册到容器中时候,用来存储的集合
        /// </summary>
        internal ConcurrentDictionary<Type, ServiceRegistry> _registries;
        /// <summary>
        /// 获取类型实例的集合
        /// </summary>
        private ConcurrentDictionary<ServiceRegistry, object> _services;
        private ConcurrentBag<IDisposable> _disposables;
        /// <summary>
        ///  Dispose()时候设置为true
        /// </summary>
        private volatile bool _disposed;
    
        /// <summary>
        /// 根容器的构造函数
        /// </summary>
        public Cat()
        {
            _registries = new ConcurrentDictionary<Type, ServiceRegistry>();
            _root = this;
            _services = new ConcurrentDictionary<ServiceRegistry, object>();
            _disposables = new ConcurrentBag<IDisposable>();
        }
    
        /// <summary>
        /// 创建子容器的构造函数
        /// </summary>
        /// <param name="parent"></param>
        internal Cat(Cat parent)
        {
            _root = parent._root;
            _registries = _root._registries;
            _services = new ConcurrentDictionary<ServiceRegistry, object>();
            _disposables = new ConcurrentBag<IDisposable>();
        }
    
        public Cat Register(ServiceRegistry registry)
        {
            EnsureNotDisposed();
            //判断是否有同类型的注册
            if (_registries.TryGetValue(registry.ServiceType, out var existing))
            {
                _registries[registry.ServiceType] = registry;
                registry.Next = existing;
            }
            else
            {
                _registries[registry.ServiceType] = registry;
            }
            return this;
        }
    
        /// <summary>
        /// 如果没注册过类型,会返回null
        /// </summary>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        public object GetService(Type serviceType)
        {
            EnsureNotDisposed();
            if (serviceType == typeof(Cat))
            {
                return this;
            }
            ServiceRegistry registry;
            if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                var elementType = serviceType.GetGenericArguments()[0];
                if (!_registries.TryGetValue(elementType, out  registry))
                {
                    //没有就直接创建
                    return Array.CreateInstance(elementType, 0);
                }
    
                var registries = registry.AsEnumerable();
                object[] services = registries.Select(it => GetServiceCore(it, new Type[0])).ToArray();
                //创建一个数组,然后把所有注册到容器中的工厂方法的实例返回
                Array array = Array.CreateInstance(elementType, services.Length);
                services.CopyTo(array, 0);
                return array;
            }
    
            if (serviceType.IsGenericType && !_registries.ContainsKey(serviceType))
            {
                var definition = serviceType.GetGenericTypeDefinition();
                return _registries.TryGetValue(definition, out registry)
                    //泛型需要获取对应类型参数
                    ? GetServiceCore(registry, serviceType.GetGenericArguments())
                    : null;
            }
    
            return _registries.TryGetValue(serviceType, out registry)
                    ? GetServiceCore(registry, new Type[0])
                    : null;
        }
    
        /// <summary>
        /// 实际进行实例创造的方法
        /// </summary>
        /// <param name="registry"></param>
        /// <param name="genericArguments"></param>
        /// <returns></returns>
        private object GetServiceCore(ServiceRegistry registry, Type[] genericArguments)
        {
            var serviceType = registry.ServiceType;
            //方法内部的方法,我还真的很少这么写。
            object GetOrCreate(ConcurrentDictionary<ServiceRegistry, object> services, ConcurrentBag<IDisposable> disposables)
            {
                if (services.TryGetValue(registry, out var service))
                {
                    return service;
                }
                service = registry.Factory(this, genericArguments);
                services[registry] = service;
                var disposable = service as IDisposable;
                if (null != disposable)
                {
                    disposables.Add(disposable);
                }
                return service;
            }
    
            switch (registry.Lifetime)
            {
                case Lifetime.Singlelton: return GetOrCreate(_root._services, _root._disposables);   
                case Lifetime.Self: return GetOrCreate(_services, _disposables);
                default:
                    //如果是Transient类型,直接创建实例,然后判断是否是IDisposable,如果是加入IDisposable数组
                    //会在容器Disposable时候,遍历Disposable
                    {
                        var service = registry.Factory(this, genericArguments);
                        var disposable = service as IDisposable;
                        if (null != disposable)
                        {
                            _disposables.Add(disposable);
                        }
                        return service;
                    }
            }
        }
    
        public void Dispose()
        {
            _disposed = true;
            foreach(var disposable in _disposables)
            {
                disposable.Dispose();
            }
            while (!_disposables.IsEmpty)
            {
                _disposables.TryTake(out _);
            }
            _services.Clear();
        }
    
        private void EnsureNotDisposed()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("Cat");
            }
        }
    }
    Cat

    主容器的扩展方法

    public static class CatExtensions
    {
        public static IEnumerable<T> GetServices<T>(this Cat cat) => cat.GetService<IEnumerable<T>>();
        public static T GetService<T>(this Cat cat) => (T)cat.GetService(typeof(T));
        public static bool HasRegistry<T>(this Cat cat) => cat.HasRegistry(typeof(T));
        public static bool HasRegistry(this Cat cat, Type serviceType) => cat._root._registries.ContainsKey(serviceType);
        public static Cat Register(this Cat cat, Type from, Type to, Lifetime lifetime)
        {
            Func<Cat, Type[], object> factory = (x, arguments) => Create(x, to, arguments);
            cat.Register(new ServiceRegistry(from, lifetime, factory));
            return cat;
        }
        public static Cat Register<TFrom, TTo>(this Cat cat, Lifetime lifetime)
            where TTo : TFrom
        {
            Func<Cat, Type[], object> factory = (_, arguments) => Create(_, typeof(TTo), arguments);
            cat.Register(new ServiceRegistry(typeof(TFrom), lifetime, factory));
            return cat;
        }
    
        public static Cat Register<TServiceType>(this Cat cat, TServiceType instance)
        {
            Func<Cat, Type[], object> factory = (_, arguments) => instance;
            cat.Register(new ServiceRegistry(typeof(TServiceType), Lifetime.Singlelton, factory));
            return cat;
        }
    
        public static Cat Register<TServiceType>(this Cat cat, Func<Cat, TServiceType> factory, Lifetime lifetime)
        {
            cat.Register(new ServiceRegistry(typeof(TServiceType), lifetime, (_, arguments) => factory(_)));
            return cat;
        }
    
        public static Cat CreateChild(this Cat cat) => new Cat(cat);
    
        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($"Cannot create the instance of {type} which does not have an public constructor.");
            }
            var constructor = constructors.FirstOrDefault(it => it.GetCustomAttributes(false).OfType<InjectionAttribute>().Any());
            //空的时候用第一个
            constructor = constructor ?? constructors.First();
            var parameters = constructor.GetParameters();
            if (parameters.Length == 0)
            {
                return Activator.CreateInstance(type);
            }
            var arguments = new object[parameters.Length];
            //递归调用所有的parameters
            for (int index = 0; index < arguments.Length; index++)
            {
                var parameter = parameters[index];
                var parameterType = parameter.ParameterType;
                if (cat.HasRegistry(parameterType))
                {
                    arguments[index] = cat.GetService(parameterType);
                }
                else if (parameter.HasDefaultValue)
                {
                    arguments[index] = parameter.DefaultValue;
                }
                else//没有注册也没有默认的情况下,会抛出异常
                {
                    throw new InvalidOperationException($"Cannot create the instance of {type} whose constructor has non-registered parameter type(s)");
                }
            }
            return Activator.CreateInstance(type, arguments);
        }
    }
    CatExtensions

    Core依赖注入

      CORE框架的依赖注入实现肯定比我们以上的实现复杂很多,他里面有实例的生命周期管理,ServiceProvider树等。

           不过最基本的体系在我们上面的例子中都已经有介绍。

           本文参考Artech大神的依赖注入文章和core源码。

  • 相关阅读:
    jquery点击添加样式,再点击取出样式
    mongodb固定集合,建立管理员安全验证
    mongodb账号安全操作
    关于新闻,在线编辑器建表时此字段一定要为text
    创建外键表时同一个数据库中外键的名字不能用同一个
    a标签鼠标经过,字颜色和下划线的颜色都变红
    zend 快捷键
    mongodb 的安装和使用
    pdo文字水印类,验证码类,缩略图类,logo类
    erlang通讯解析浮点数的一些问题
  • 原文地址:https://www.cnblogs.com/qixinbo/p/12844958.html
Copyright © 2011-2022 走看看