• net5学习笔记---依赖注入


    小王的故事

    小王去上班

    ​ 小王是个程序员,每个工作日他都要去上班,诸多交通工具他最喜欢的交通工具是骑电车。在骑行的过程中放空自己使他很快。
    ​ 突然有一天天气预报说近期有很大的雨,小王再想骑电车去上班显然是不可能了。那么他就要选择开汽车去。
    但是由于小王每天过于依赖骑电动车,开汽车水平有限,那么他就要重新学习开汽车。
    ​ 因此小王很烦恼,我就想去上个班,还要掌握不同的交通工具,这真是让人烦恼,难道我就想做一个单纯的打工族就这么难吗?小王就把这件事告诉了他的老婆,她老婆是个老司机,她说“这事你就不用管了,我是老司机啊,开车这事我来控制就好了,你就记得给我多买些礼物就好“。从此之后小王就安心的为了老婆的赚钱,开车这事就完全有他老婆负责。

    小王的日记:

    ​ 我曾经很享受自己去上班(自己控制),每天去上班就拿出我心爱的电动车(实例化对象),直到有一天天气预报告诉我要下大雨,再依赖自行车就会被大雨淋着,而换交通工具我发现我就要重新学习开车(高耦合),知道我老婆大人说她是老司机(ioc容器),开车这事由她来控制(控制反转),不管怎么去上班,事先告诉他用什么交通工具就行(依赖注入),从此我每个工作日只要叫上我老婆她就直接带我去上班了。


    依赖注入

    ​ 从小王的故事我们可以看到一些关键词依赖控制 而今天要说的就是什么是依赖注入。说到依赖注入(DI)还有个概念叫控制反转(IOC)。
    控制反转(IOC—Inversion of Control)不是什么技术,而是一种设计思想。它的思路是设计好的依赖类交给容器控制,而不是在对象中直接实例化控制。就是在系统运行时ioc容器动态向一个对象提供他依赖的其他对象。而他的实现就是用依赖注入来实现。
    依赖注入(DI—Dependency Injection)是组件之间依赖关系由容器决定。为此我们要明白依赖和注入关系
    依赖:由于应用程序需要ioc容器提供对象外部的资源所以应用程序依赖于ioc容器
    注入:某个对象所需要的外部资源(包括对象、资源、常量数据)注入到了ioc容器中。


    net5 内置依赖注入

    依赖注入是net core和net5的核心概念,我们在了解完概念之后也要对我们框架中对他的实现有个清楚的认识。当然官方文档是最好的了解方式。接下来我从我的角度去说一下对net 5(net core)依赖注入的理解.首先我们要知道net5依赖注入的实现由net 内置的容器和第三方的容器。我们主要说内置的实现情况。

    DependencyInjection

    net5 内置依赖注入主要用到了Microsoft.Extensions.DependencyInjectionMicrosoft.Extensions.DependencyInjection.Abstraction两个引用。我们nuget引用就行。它们是开源的。
    DependencyInjectionClass
    我们可以看到DependencyInjection这个项目并不大,但却是整个net5(NET Core)的基础,因为它提供了依赖注入容器的默认实现,而依赖注入是net5(net core)的核心基础.

    源码解析

    image-20210515133637360

    IServiceCollection

    public class ServiceCollection : IServiceCollection
    {
         //ServiceDescriptor缓存集合
         private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
         //注册到当前ServiceCollection对象中的ServiceDescriptor数量
         public int Count => _descriptors.Count;
         public bool IsReadOnly => false;
         public ServiceDescriptor this[int index]
         {
             get
             {
                 return _descriptors[index];
             }
             set
             {
                 _descriptors[index] = value;
             }
         }
         //清空所有ServiceDescriptor对象
         public void Clear() 
         {
             _descriptors.Clear();
         }
         //查询ServiceCollection是否包含指定ServiceDescriptor对象
         public bool Contains(ServiceDescriptor item)=> _descriptors.Contains(item);
         //拷贝ServiceDescriptor
         public void CopyTo(ServiceDescriptor[] array, int arrayIndex) =>_descriptors.CopyTo(array, arrayIndex);
         //从ServiceCollection移除指定ServiceDescriptor
         public bool Remove(ServiceDescriptor item)=>_descriptors.Remove(item);
         //获取此ServiceCollection的迭代器
         public IEnumerator<ServiceDescriptor> GetEnumerator()=> _descriptors.GetEnumerator();
         public int IndexOf(ServiceDescriptor item) => _descriptors.IndexOf(item);
         public void Insert(int index, ServiceDescriptor item) => _descriptors.Insert(index, item);
         public void RemoveAt(int index)=> _descriptors.RemoveAt(index);
    }
    

    IServiceCollection&&ServiceDescriptor

    namespace Microsoft.Extensions.DependencyInjection
    {
        /// <summary>
        /// 指定服务描述符集合的约定
        /// </summary>
        public interface IServiceCollection : IList<ServiceDescriptor>
        {
        }
    }
    

    我们从代码中可以看到内置依赖注入,它的内置容器是ServiceProvider,我们会把我们的服务注入到ServiceProvider 中来,而IServiceCollection是ServiceProvider的list的集合。

    ServiceDescriptor

         public static IServiceCollection Add(this IServiceCollection collection,IEnumerable<ServiceDescriptor> descriptors)
            {
                if (collection == null)
                {
                    throw new ArgumentNullException(nameof(collection));
                }
                if (descriptors == null)
                {
                    throw new ArgumentNullException(nameof(descriptors));
                }
                foreach (var descriptor in descriptors)
                {
                    collection.Add(descriptor);
                }
                return collection;
            }
    

    我们可以看到服务注册的时候,提供了ServiceDescriptor

     /// <summary>
        /// Describes a service with its service type, implementation, and lifetime.
        /// </summary>
        [DebuggerDisplay("Lifetime = {Lifetime}, ServiceType = {ServiceType}, ImplementationType = {ImplementationType}")]
        public class ServiceDescriptor
        {}
    

    它是对服务的类型、生命周期、获取服务的方式的描述。
    image-20210515103733823
    类型

    //注册的类型的生命周期
    public ServiceLifetime Lifetime { get; }
    //基类型
    public Type ServiceType { get; }
    //实例类型(派生类型)
    public Type ImplementationType { get; }
    //实例对象
    public object ImplementationInstance { get; }
    //注册类型实例化对象的工厂
    public Func<IServiceProvider, object> ImplementationFactory { get; }
    

    构造函数

    //派生类型
    public ServiceDescriptor(Type serviceType,object instance)
         : this(serviceType, ServiceLifetime.Singleton)
    {
          Lifetime = lifetime;
          ServiceType = serviceType;
          ImplementationInstance = instance;
    }
     //工厂
    public ServiceDescriptor(Type serviceType,Func<IServiceProvider, object> factory,ServiceLifetime lifetime)
         : this(serviceType, lifetime)
    {
          Lifetime = lifetime;
          ServiceType = serviceType;
          ImplementationFactory = factory;
    }
    //具体实例对象
    public ServiceDescriptor(Type serviceType,Type implementationType,ServiceLifetime lifetime)
         : this(serviceType, lifetime)
    {
          Lifetime = lifetime;
          ServiceType = serviceType;
          ImplementationType = implementationType;
    }
    
    /// <summary>
    ///获取当前注册类型的实例类型(内部类)
    /// </summary>
    /// <returns></returns>
    internal Type GetImplementationType(){}
    //真正实例化对象的方法,重载都是调用此类方法 
    public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime){}
    public static ServiceDescriptor Singleton(Type serviceType,object implementationInstance){}
    public static ServiceDescriptor Scoped(Type service, Func<IServiceProvider, object> implementationFactory){}
    

    ServiceCollectionServiceExtensions&ServiceCollectionDescriptorExtensions&ServiceCollectionContainerBuilderExtensions

    这三个方法时ServiceCollection的三个扩展方法分别实现:我们所使用了注册方式;TryAdd和RemoveAll,Replace等操作和构造ServiceProvider实例。
    ServiceCollectionServiceExtensions:
    代码太多(不同生命周期的注册)就不贴出了,截个图算了。
    image-20210515121957047
    ServiceCollectionDescriptorExtensions:
    image-20210515122419417

    public static void TryAdd(this IServiceCollection collection,ServiceDescriptor descriptor)
    {
         if (!collection.Any(d => d.ServiceType == descriptor.ServiceType))
              collection.Add(descriptor);
    }
    

    注:Add,RemoveAll,Replace,没什么好说的,其中TryAdd,TryAddEnumerable需要注意到的是:中有服务集合中不存在才会进行过注册。
    ServiceCollectionContainerBuilderExtensions

        public static class ServiceCollectionContainerBuilderExtensions
        {
            public static ServiceProvider BuildServiceProvider(this IServiceCollection services)     
            public static ServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes)    
            public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options)
            
    }
    

    IServiceProvider

    public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback
    {
        //	ServiceProvider的扩展接口  
        //	使用这个接口的子类进行调用缓存各种注册服务和调用访问者对象进行获取实例对象
         private readonly IServiceProviderEngine _engine;  
         /// 缓存类型(访问者模式)
         private readonly CallSiteValidator _callSiteValidator;
         internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
         {
              IServiceProviderEngineCallback callback = null;
              if (options.ValidateScopes)
              {
                   callback = this;
                   _callSiteValidator = new CallSiteValidator();
              }
              //实例化工作引擎类型
              switch (options.Mode)
              {
                   case ServiceProviderMode.Dynamic:
                        // 实例化 DynamicServiceProviderEngine
                        _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
                        break;
                   case ServiceProviderMode.Runtime:
                        _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
                        break;
                        case ServiceProviderMode.ILEmit:
                        _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
                   break;
                        case ServiceProviderMode.Expressions:
                        _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
                        break;
                   default:
                   	throw new NotSupportedException(nameof(options.Mode));
              }
         }
         // 获取指定类型的服务对象
         public object GetService(Type serviceType) => _engine.GetService(serviceType);
         public void Dispose() => _engine.Dispose();
        //创建服务实例缓存
    	void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite)
           =>_callSiteValidator.ValidateCallSite(callSite);
        //服务实例的校验
         void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope)
          =>_callSiteValidator.ValidateResolution(serviceType, scope, _engine.RootScope);
    }
    

    我们可以看到ServiceProvider用来获取服务的实例,它提供了一个扩展IServiceProviderEngine来实现其功能。x下面我们来看一下源码

    public static class ServiceProviderServiceExtensions
    {
    	 //	从IServiceProvider获取“T”类型的服务。  
         public static T GetService<T>(this IServiceProvider provider)
              => (T)provider.GetService(typeof(T));
         
         //	从IServiceProvider获取“T”类型的服务。
         public static object GetRequiredService(this IServiceProvider provider, Type serviceType)
         {
              // 如果当前ServiceProvider实现了 ISupportRequiredService 
              // 则直接调用当前ServiceProvier的GetRequiredService获取服务实例
              var requiredServiceSupportingProvider = provider as ISupportRequiredService;
              if (requiredServiceSupportingProvider != null)
                 return requiredServiceSupportingProvider.GetRequiredService(serviceType);
              //如果当前ServiceProvider未实现ISupportRequiredService
              //就直接调用GetService获取服务实例,但是如果服务实例为空,则抛出异常
              var service = provider.GetService(serviceType);
              if (service == null)
                  throw new InvalidOperationException(Resources.FormatNoServiceRegistered(serviceType));
              return service;
         }
         public static T GetRequiredService<T>(this IServiceProvider provider)
              => (T)provider.GetRequiredService(typeof(T));
         //获取指定注册类型<T>的所有服务实例
         public static IEnumerable<T> GetServices<T>(this IServiceProvider provider)
              => provider.GetRequiredService<IEnumerable<T>>();
         //获取指定注册类型<T>的所有服务实例
         public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType)
         {
              //制造一个serviceType类型的IEnumberable<>集合,serviceTypele类型作为当前集合的泛型参数
              var genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
              return (IEnumerable<object>)provider.GetRequiredService(genericEnumerable);
         }
    
         //创建一个子IServiceProvider(容器)实例
         public static IServiceScope CreateScope(this IServiceProvider provider)
              => provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
    }
    

    NET 内置依赖注入实现

    我们再Startup.ConfigureServices(IServiceCollection services)进行注册下面我直接用代码来看一下实现

    public class UserService : IUserService
    {
        public string GetName()
        {
            return "王延领";
        }
    }
    public interface IUserService
    {
        string GetName();
    }
    
    

    1.面向接口注册

    services.AddScoped(typeof(IUserService), typeof(UserService));
    services.AddScoped<IUserService, UserService>();
    

    2.实现形式注册

    services.AddScoped<UserService>();
    services.AddScoped(typeof(UserService));
    

    两种形式都可以,但扩展性显然是第一种好。

    3.批量注入

    var assembly = Assembly.GetExecutingAssembly()
                            .DefinedTypes
                            .Where(a => a.Name.EndsWith("Service") && !a.Name.StartsWith("I"));
    foreach (var item in assembly)
    {
        services.AddScoped(item.GetInterfaces().FirstOrDefault(), item);
    }
    

    第三方容器(Autofact)注入

    后续单独一篇文章再写吧。

  • 相关阅读:
    [K/3Cloud] 关于单据转换的问题
    [K/3Cloud] 分录行复制和新增行的冲突如何处理
    [K/3Cloud] 动态表单打开时传递一个自定义参数并在插件中获取
    [译]C++书籍终极推荐
    Time.deltaTime 含义和应用
    cocos2d-x 锚点,位置==》动手实验记录 多动手... :)
    iOS的主要框架介绍
    xcode 预编译头文件
    android 内部存储相关知识点: getfilestreampath getDir 子文件夹
    coco2d-js 多屏适配相关API
  • 原文地址:https://www.cnblogs.com/wyl1924/p/14771489.html
Copyright © 2020-2023  润新知