相信使用過Asp.Net Core開發框架的人對自帶的DI框架已經至關熟悉了,不少剛開始接觸.Net Core的時候以爲不適應,主要就是由於Core默認集成它的緣由。它是Asp.Net Core基礎核心框架之一,對於Asp.Net Core來講DI就靈魂,已經深刻到這框架的骨髓裏了。對於IOC和DI,可能每一個人都能說出本身的理解。IOC全稱是Inversion of Control翻譯成中文叫控制反轉,簡單的說就是把對象的控制權反轉到IOC容器中,由IOC管理其生命週期。DI全稱是DependencyInjection翻譯成中文叫依賴注入,就是IOC容器把你依賴的模塊經過注入的方式提供給你,而不是你本身主動去獲取,其形式主要分爲構造注入和屬性注入,Core自帶的DI只支持構造注入,至於爲何,最多的說法就是構造注入能使得依賴變得更清晰,我既然依賴你,那麼我實例化的時候你就必須得出現。而構造函數偏偏就承擔着這種責任。git
不少人接觸它的時候應該都是從Asp.Net Core學習過程當中開始的。其實它自己對Asp.Net Core並沒有依賴關係,Asp.Net Core依賴DI,可是這套框架自己並不僅是能夠提供給Asp.Net Core使用,它是一套獨立的框架,開源在微軟官方Github的extensions倉庫中具體地址是https://github.com/dotnet/extensions/tree/v3.1.5/src/DependencyInjection。關於如何使用,這裏就再也不多說了,相信你們都很是清楚了。那我們就說點不同的。github
咱們都知道提供註冊的服務名稱叫IServiceCollection,咱們大部分狀況下主要使用它的AddScoped、AddTransient、AddSingleton來完成註冊。咱們就先查看一下IServiceCollection接口的具體實現,找到源碼位置框架
public interface IServiceCollection : IList<ServiceDescriptor> { }
(⊙o⊙)…額,你並無看錯,此次我真沒少貼代碼,其實IServiceCollection本質就是IList
/// <summary> /// 經過泛型註冊 /// </summary> public static IServiceCollection AddTransient<TService, TImplementation>(this IServiceCollection services) where TService : class where TImplementation : class, TService { if (services == null) { throw new ArgumentNullException(nameof(services)); } //獲得泛型類型 return services.AddTransient(typeof(TService), typeof(TImplementation)); } /// <summary> /// 根據類型註冊 /// </summary> public static IServiceCollection AddTransient( this IServiceCollection services, Type serviceType, Type implementationType) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (serviceType == null) { throw new ArgumentNullException(nameof(serviceType)); } if (implementationType == null) { throw new ArgumentNullException(nameof(implementationType)); } return Add(services, serviceType, implementationType, ServiceLifetime.Transient); } /// <summary> /// 根據類型實例來自工廠註冊方法 /// </summary> public static IServiceCollection AddTransient( this IServiceCollection services, Type serviceType, Func<IServiceProvider, object> implementationFactory) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (serviceType == null) { throw new ArgumentNullException(nameof(serviceType)); } if (implementationFactory == null) { throw new ArgumentNullException(nameof(implementationFactory)); } return Add(services, serviceType, implementationFactory, ServiceLifetime.Transient); }
經過以上代碼咱們能夠獲得兩個結論,一是註冊服務的方法本質都是在調用Add重載的兩個方法,二是聲明週期最終仍是經過ServiceLifetime來控制的AddScoped、AddTransient、AddSingleton只是分文別類的進行封裝而已,咱們來看ServiceLifetime的源碼實現函數
public enum ServiceLifetime { /// <summary> /// 指定將建立服務的單個實例。 /// </summary> Singleton, /// <summary> /// 指定每一個做用域建立服務的新實例。 /// </summary> Scoped, /// <summary> /// 指定每次請求服務時都將建立該服務的新實例。 /// </summary> Transient }
這個枚舉是爲了枚舉咱們註冊服務實例的聲明週期的,很是清晰不在過多講述,接下來咱們看核心的兩個Add方法的實現學習
private static IServiceCollection Add( IServiceCollection collection, Type serviceType, Type implementationType, ServiceLifetime lifetime) { var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime); collection.Add(descriptor); return collection; } private static IServiceCollection Add( IServiceCollection collection, Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime) { var descriptor = new ServiceDescriptor(serviceType, implementationFactory, lifetime); collection.Add(descriptor); return collection; }
經過這兩個核心方法咱們能夠很是清晰的瞭解到註冊的本質其實就是構建ServiceDescriptor實例而後添加到IServiceCollection即IList
public static IServiceCollection AddTransient( this IServiceCollection services, Type serviceType) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (serviceType == null) { throw new ArgumentNullException(nameof(serviceType)); } //把本身註冊給本身 return services.AddTransient(serviceType, serviceType); }
經過這個方法咱們就能夠看到其實註冊單類型的方法,也是經過調用的注入實例到抽象的方法,只不過是將本身註冊給了本身。
好了,抽象和擴展方法咱們就先說到這裏,接下來咱們來看IServiceCollection的實現類ServiceCollection的實現this
public class ServiceCollection : IServiceCollection { private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>(); public int Count => _descriptors.Count; public bool IsReadOnly => false; public ServiceDescriptor this[int index] { get { return _descriptors[index]; } set { _descriptors[index] = value; } } public void Clear() { _descriptors.Clear(); } public bool Contains(ServiceDescriptor item) { return _descriptors.Contains(item); } public void CopyTo(ServiceDescriptor[] array, int arrayIndex) { _descriptors.CopyTo(array, arrayIndex); } public bool Remove(ServiceDescriptor item) { return _descriptors.Remove(item); } public IEnumerator<ServiceDescriptor> GetEnumerator() { return _descriptors.GetEnumerator(); } void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item) { _descriptors.Add(item); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public int IndexOf(ServiceDescriptor item) { return _descriptors.IndexOf(item); } public void Insert(int index, ServiceDescriptor item) { _descriptors.Insert(index, item); } public void RemoveAt(int index) { _descriptors.RemoveAt(index); } }
這個類就很是清晰,也很是簡單了。ServiceCollection承載了一個List
public class ServiceDescriptor { public ServiceDescriptor( Type serviceType, Type implementationType, ServiceLifetime lifetime) : this(serviceType, lifetime) { ImplementationType = implementationType; } public ServiceDescriptor( Type serviceType, object instance) : this(serviceType, ServiceLifetime.Singleton) { ImplementationInstance = instance; } public ServiceDescriptor( Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime lifetime) : this(serviceType, lifetime) { ImplementationFactory = factory; } private ServiceDescriptor(Type serviceType, ServiceLifetime lifetime) { Lifetime = lifetime; ServiceType = serviceType; } public ServiceLifetime Lifetime { get; } public Type ServiceType { get; } public Type ImplementationType { get; } public object ImplementationInstance { get; } public Func<IServiceProvider, object> ImplementationFactory { get; } }
這裏咱們只是粘貼了初始化的方法,經過這個初始化咱們獲得了,本質其實就是給描述具體註冊的Lifetime、ServiceType、ImplementationType、ImplementationInstance、ImplementationFactory賦值。在平時的使用中,咱們在註冊服務的時候還會用到這種註冊方式翻譯
services.Add(ServiceDescriptor.Scoped<IPersonService, PersonService>()); //services.Add(ServiceDescriptor.Scoped(typeof(IPersonService),typeof(PersonService))); //或 services.Add(ServiceDescriptor.Transient<IPersonService, PersonService>()); //services.Add(ServiceDescriptor.Transient(typeof(IPersonService), typeof(PersonService))); //或 services.Add(ServiceDescriptor.Singleton<IPersonService, PersonService>()); //services.Add(ServiceDescriptor.Singleton(typeof(IPersonService), typeof(PersonService)));
這種註冊方式是經過ServiceDescriptor自身的操做去註冊相關實例,咱們拿出來其中一個Transient看一下具體實現
public static ServiceDescriptor Transient<TService, TImplementation>() where TService : class where TImplementation : class, TService { //都是在調用Describe return Describe<TService, TImplementation>(ServiceLifetime.Transient); } public static ServiceDescriptor Transient(Type service, Type implementationType) { //都是在調用Describe return Describe(service, implementationType, ServiceLifetime.Transient); } public static ServiceDescriptor Describe(Type serviceType, Type implementationType, ServiceLifetime lifetime) { //仍是返回ServiceDescriptor實例 return new ServiceDescriptor(serviceType, implementationType, lifetime); } public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime) { //仍是返回ServiceDescriptor實例 return new ServiceDescriptor(serviceType, implementationFactory, lifetime); }
經過這個咱們就能夠了解到ServiceDescriptor.Scoped、ServiceDescriptor.Singleton、ServiceDescriptor.Singleton實際上是調用的Describe方法,Describe的自己仍是去實例化ServiceDescriptor,異曲同工,只是多了種寫法,最終仍是去構建ServiceDescriptor。經過這麼多源碼的分析得出的結論就一點IServiceCollection註冊的本質就是在構建ServiceDescriptor集合。
上面咱們瞭解到了服務註冊相關,至於服務是怎麼提供出來的,你們應該都是很是熟悉了實際上是根據IServiceCollection構建出來的
IServiceProvider serviceProvider = services.BuildServiceProvider();
BuildServiceProvider並非IServiceCollection的自帶方法,因此也是來自擴展方法,找到ServiceCollectionContainerBuilderExtensions擴展類,最終都是在執行這個方法
public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options) { return new ServiceProvider(services, options); }
BuildServiceProvider的時候須要傳遞ServiceProviderOptions這個類主要是配置是否校驗做用域和提供的實例來自於那種提供引擎使用
public class ServiceProviderOptions { internal static readonly ServiceProviderOptions Default = new ServiceProviderOptions(); /// <summary> /// 是夠在編譯的時候校驗做用域範圍檢查 /// </summary> public bool ValidateScopes { get; set; } /// <summary> /// 是夠在編譯的時候校驗做用域範圍檢查 /// </summary> public bool ValidateOnBuild { get; set; } /// <summary> /// 配置使用那種方式提供ServiceProvider的承載的具體實例 /// </summary> internal ServiceProviderMode Mode { get; set; } = ServiceProviderMode.Default; } internal enum ServiceProviderMode { Default, Dynamic, Runtime, Expressions, ILEmit }
做用域範圍檢查仍是很是嚴格的,不開啓的也會有必定的依賴規則,簡單總結一下
public interface IServiceProvider { object GetService (Type serviceType); }
ServiceProvider是IServiceProvider的默認實現類,它是獲取註冊實例的默認出口類,咱們只看提供服務相關的
public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback, IAsyncDisposable { 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(); } //根據ServiceProviderMode的值判斷纔有那種方式去實例化對象 switch (options.Mode) { //默認方式 case ServiceProviderMode.Default: if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled")) { _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); } else { _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); } break; case ServiceProviderMode.Dynamic: _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); break; case ServiceProviderMode.Runtime: _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); break; //if IL_EMIT 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)); } //判斷是否開啓編譯時範圍校驗 if (options.ValidateOnBuild) { List<Exception> exceptions = null; foreach (var serviceDescriptor in serviceDescriptors) { try { _engine.ValidateService(serviceDescriptor); } catch (Exception e) { } } } } /// <summary> /// 經過IServiceProviderEngine獲取具體實例的方法 /// </summary> public object GetService(Type serviceType) => _engine.GetService(serviceType); }
在這個類裏,關於提供具體實例的操做仍是很是清晰的,關於更深的IServiceProviderEngine這裏就不過多介紹了,有興趣的能夠自行在GitHub上查閱。
在聲明週期裏Scope是比較特殊也是比較抽象的一個,咱們使用的時候是經過當前serviceProvider建立子做用域
using (IServiceScope scope = serviceProvider.CreateScope()) { IServiceProvider scopeProvider = scope.ServiceProvider; }
它大概的思路就是在當前容器中建立一個做用域,scope.ServiceProvider來獲取這個子容器做用域裏的實例。Singleton類型的實例直接去根容器獲取,因此和當前子容器做用域無關。Scoped類型的實例,在當前做用域內惟一,不管獲取多少次返回的都是同一個實例。Transient類型的只要去獲取都是返回新的實例。當前IServiceScope釋放的時候Scoped類型的實例也會被釋放,注意!!!Transient類型的實例也是在當前IServiceScope Dispose的時候去釋放,儘管你每次獲取的時候都是新的實例,可是釋放的時候都是統一釋放的。在當前ServiceScope內你能夠繼續建立當前Scope的IServiceScope。其實經過這裏也不難發現根容器的Scoped其實就是等同於Singleton,其生命週期都是和應用程序保持一致。
Scope問題在若是寫控制檯之類的程序其做用可能不是很明顯,除非有特殊的要求,在Asp.Net Core中使用仍是比較深刻的。Asp.Net Core在啓動的時候會建立serviceProvider,這個serviceProvider的Scope是跟隨程序的生命週期一致的,它是做爲全部服務實例的根容器。在Asp.Net Core中有幾種狀況的實例和請求無關也就是說在程序運行期間是單例狀況的,咱們使用的時候須要注意的地方
UseServiceProviderFactory方法主要是爲咱們提供了替換默認容器的操做,經過這個方法能夠將三方的IOC框架結合進來好比Autofac。咱們能夠查看UseServiceProviderFactory具體的實現,瞭解它的工做方式。這個方法來自HostBuilder類
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory) { _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory))); return this; }
咱們找到_serviceProviderFactory定義的地方,默認值就是爲ServiceFactoryAdapter傳遞了DefaultServiceProviderFactory實例。
private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
繼續查找ServiceFactoryAdapter的大體核心實現
internal class ServiceFactoryAdapter<TContainerBuilder> : IServiceFactoryAdapter { private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory; public object CreateBuilder(IServiceCollection services) { return _serviceProviderFactory.CreateBuilder(services); } public IServiceProvider CreateServiceProvider(object containerBuilder) { return _serviceProviderFactory.CreateServiceProvider((TContainerBuilder)containerBuilder); } }
經過查找HostBuilder中這段源碼咱們能夠知道ServiceFactoryAdapter建立出來的容器是供整個Host使用的。也就是說咱們在程序中使用的容器相關的都是由它提供的。
接下來咱們看下默認的DefaultServiceProviderFactory的大體實現。找到源碼位置
public class DefaultServiceProviderFactory : IServiceProviderFactory<IServiceCollection> { public IServiceCollection CreateBuilder(IServiceCollection services) { return services; } public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder) { return containerBuilder.BuildServiceProvider(_options); } }
沒啥邏輯,其實就是把默認的IServiceCollection和IServiceProvider經過工廠的形式提供出來。這麼作的目的只有一個,就是下降依賴的耦合度方便咱們可以介入第三方的IOC框架。口說無憑,接下來咱們就看一下Autofac是怎麼適配進來的。咱們在GitHub上找到Autofac.Extensions.DependencyInjection倉庫的位置https://github.com/autofac/Autofac.Extensions.DependencyInjection,找到Autofac中IServiceProviderFactory實現類AutofacServiceProviderFactory,看看他是如何適配到默認的IOC框架的
public class AutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder> { private readonly Action<ContainerBuilder> _configurationAction; public AutofacServiceProviderFactory(Action<ContainerBuilder> configurationAction = null) { _configurationAction = configurationAction ?? (builder => { }); } public ContainerBuilder CreateBuilder(IServiceCollection services) { //因爲是使用Autofac自己的容器去工做,因此返回的Autofac承載類ContainerBuilder var builder = new ContainerBuilder(); //將現有的IServiceCollection中註冊的實例託管到ContainerBuilder中 builder.Populate(services); //這一步是咱們自定義注入到Autofac方法的委託,及咱們在Startup類中定義的 //public void ConfigureContainer(ContainerBuilder builder)方法 _configurationAction(builder); return builder; } public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder) { if (containerBuilder == null) throw new ArgumentNullException(nameof(containerBuilder)); //獲取Container容器,由於接下來要使用獲取實例的方法了 var container = containerBuilder.Build(); //這個類實現了IServiceProvider接口 //實現了public object GetService(Type serviceType)方法從Autofac的Container中獲取實例 return new AutofacServiceProvider(container); } }
IServiceProviderFactory的工做其實就是適配符合咱們使用的適配器模式,其核心就是用你的容器去託管註冊到IServiceCollection中的服務。而後用你的容器去構建IServiceProvider實例。
經過以上咱們對自帶的DependencyInjection工做方式有了必定的瞭解,並且其擴展性很是強,可以使咱們經過本身的方式去構建服務註冊和注入,咱們以Autofac爲例講解了三方容器集成到自帶IOC的方式。有不少核心的源碼並無講解到,由於怕本身理解不夠,就不誤導你們了。我在上文中涉及到源碼的地方基本上都加了源碼的鏈接,能夠直接點進去查看源碼,以前源碼探究相關的文章也都是同樣,可能以前有許多同窗沒有注意到。主要緣由是我粘貼出來的代碼有刪減,最重要的仍是怕本身理解不到位,誤導了你們,這樣就能用過點擊本身查看源碼了。若有你有更好的理解,或者以爲我講解的理解不到的地方,歡迎評論區溝通交流。