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)注入

后續單獨一篇文章再寫吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM