源碼解析.Net中DependencyInjection的實現


前言

筆者的這篇文章和上篇文章思路一樣,不注重依賴注入的使用方法,更加注重源碼的實現,我盡量的表達清楚內容,讓讀者能夠真正的學到東西。如果有不太清楚依賴注入是什么或怎么在.Net項目中使用的話,請點擊這里,這是微軟的官方文檔,把用法介紹的很清晰了,相信你會有很大收獲。那么廢話不多說,咱們進入正題(可能篇幅有點長,耐心讀完你會有收獲的😁)。

DependencyInjection類之間的關系

下圖中只列舉重要的類和接口(實際的類和接口有很多),里面的方法和屬性也只列出重要的,這里只是讓你有個大概的印象,看不懂沒關系,繼續往下讀。

源碼解析

  1. 首先我們要知道,.Net程序在啟動的時候會構建一個Host(Host.Build().Run()),在其Build方法中來構建容器(只構建根容器,下面會解釋),具體在哪構建容器,源碼如下圖:
public class HostBuilder : IHostBuilder
{
    private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
    public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
    {
        _configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
        return this;
    }
    //即我們在main函數中看到的Build方法
    public IHost Build()
    {
        //只能執行一次這個方法
        if (_hostBuilt)
        {
            throw new InvalidOperationException(SR.BuildCalled);
        }
        _hostBuilt = true;
        using var diagnosticListener = new DiagnosticListener("Microsoft.Extensions.Hosting");
        const string hostBuildingEventName = "HostBuilding";
        const string hostBuiltEventName = "HostBuilt";

        if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuildingEventName))
        {
            Write(diagnosticListener, hostBuildingEventName, this);
        }
        //執行Host配置(應用程序執行路徑,增加_dotnet環境變量,獲取命令行參數,加載預配置)
        BuildHostConfiguration();
        //設置主機環境變量
        CreateHostingEnvironment();
        //設置上下文
        CreateHostBuilderContext();
        //構建程序配置(加載appsetting.json)
        BuildAppConfiguration();
        //構造容器,加載服務
        CreateServiceProvider();
        var host = _appServices.GetRequiredService<IHost>();
        if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuiltEventName))
        {
            Write(diagnosticListener, hostBuiltEventName, host);
        }

        return host;
    }
    
     private void CreateServiceProvider()
     {
        //構建服務集合
        var services = new ServiceCollection();
        services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
        services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
        services.AddSingleton(_hostBuilderContext);
        services.AddSingleton(_ => _appConfiguration);
        services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
        services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
        AddLifetime(services);
        services.AddSingleton<IHost>(_ =>
        {
            return new Internal.Host(_appServices,
                _hostingEnvironment,
                _defaultProvider,
                _appServices.GetRequiredService<IHostApplicationLifetime>(),
                _appServices.GetRequiredService<ILogger<Internal.Host>>(),
                _appServices.GetRequiredService<IHostLifetime>(),
                _appServices.GetRequiredService<IOptions<HostOptions>>());
        });
        services.AddOptions().Configure<HostOptions>(options => { options.Initialize(_hostConfiguration); });
        services.AddLogging();
        //加載StartUp里面的ConfigService方法
        foreach (Action<HostBuilderContext, IServiceCollection> configureServicesAction in _configureServicesActions)
        {
            configureServicesAction(_hostBuilderContext, services);
        }
        //構建容器
        object containerBuilder = _serviceProviderFactory.CreateBuilder(services);
        //配置容器適配器,可以構建自己的容器
        foreach (IConfigureContainerAdapter containerAction in _configureContainerActions)
        {
            containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
        }
        //生成容器服務
        _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);
        if (_appServices == null)
        {
            throw new InvalidOperationException(SR.NullIServiceProvider);
        }
        //在根容器中先生成IConfiguration
        _ = _appServices.GetService<IConfiguration>();
    }
}

從上面可以看出,_serviceProviderFactory實際上就是DefaultServiceProviderFactory對象實例,它通過調用自己的CreateServiceProvider方法,來構建默認容器,而默認容器是作為根容器存在,全局只有一個。

  1. _serviceProviderFactory.CreateBuilder(services)方法只是簡單的轉換(把IServiceCollection轉化成Object),為什么他要返回object呢?因為在.net中默認實現是個IServiceCollection,那么如果要是需要自定義容器的話,則需要通過自己的ProviderFactory返回自己的Builder,來生成容器,在這里就不細講了,以后其他文章會詳細說下怎么自定義一個IOC,這里只講默認實現,接下來我們具體講它是怎么生成容器服務的,請看CreateServiceProvider方法實現:
//.net提供的默認的ProviderFactory
public class DefaultServiceProviderFactory : IServiceProviderFactory<IServiceCollection>
{
    private readonly ServiceProviderOptions _options;
    public DefaultServiceProviderFactory() : this(ServiceProviderOptions.Default)
    {

    }
    public DefaultServiceProviderFactory(ServiceProviderOptions options)
    {
        if (options == null)
        {
            throw new ArgumentNullException(nameof(options));
        }
        _options = options;
    }
    public IServiceCollection CreateBuilder(IServiceCollection services)
    {
        return services;
    }
    //通過調用擴展方法,來實現ServiceProvider
    public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
    {
        return containerBuilder.BuildServiceProvider(_options);
    }
}
public static class ServiceCollectionContainerBuilderExtensions
{
    public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options)
    {
        if (services == null)
        {
            throw new ArgumentNullException(nameof(services));
        }

        if (options == null)
        {
            throw new ArgumentNullException(nameof(options));
        }
        
        return new ServiceProvider(services, options);
    }
}
public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDisposable
{
    //驗證Scoped服務是否有在Singleton服務中存在,默認是關閉的,因為可能會把Scoped服務提升為Singleton服務,那么這個本身就是不正常的調用,建議修改調用方法。
    private readonly CallSiteValidator _callSiteValidator;
    //用來獲取服務
    private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor;
    //服務提供的引擎
    internal ServiceProviderEngine _engine;
    //是否已被釋放
    private bool _disposed;
    //對每種type做了一個緩存,來提高效率
    private ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> _realizedServices;
    //服務描述工廠,保存着服務描述的集合,描述服務的類型
    internal CallSiteFactory CallSiteFactory { get; }
    //根容器
    internal ServiceProviderEngineScope Root { get; }

    internal static bool VerifyOpenGenericServiceTrimmability { get; } =
        AppContext.TryGetSwitch("Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability", out bool verifyOpenGenerics) ? verifyOpenGenerics : false;

    internal ServiceProvider(ICollection<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
    {
        //根容器就是本身,並且IsRootScope是true標識
        Root = new ServiceProviderEngineScope(this, isRootScope: true); 
        //獲取用那種引擎來獲取服務
        _engine = GetEngine(); 
        //獲取服務時調用的函數
        _createServiceAccessor = CreateServiceAccessor;
        _realizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>();
        //主要是生成服務的ServiceCallSite這個很重要,用來描述服務類型
        CallSiteFactory = new CallSiteFactory(serviceDescriptors);
        //額外的一些內部服務描述
        CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());
        CallSiteFactory.Add(typeof(IServiceScopeFactory), new ConstantCallSite(typeof(IServiceScopeFactory), Root));
        CallSiteFactory.Add(typeof(IServiceProviderIsService), new ConstantCallSite(typeof(IServiceProviderIsService), CallSiteFactory));

        if (options.ValidateScopes)
        {
            _callSiteValidator = new CallSiteValidator();
        }
        //在build階段就驗證服務注入是否正確
        if (options.ValidateOnBuild)
        {
            List<Exception> exceptions = null;
            foreach (ServiceDescriptor serviceDescriptor in serviceDescriptors)
            {
                try
                {
                    ValidateService(serviceDescriptor);
                }
                catch (Exception e)
                {
                    exceptions = exceptions ?? new List<Exception>();
                    exceptions.Add(e);
                }
            }

            if (exceptions != null)
            {
                throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray());
            }
        }
    }

    public object GetService(Type serviceType) => GetService(serviceType, Root);

    private void OnCreate(ServiceCallSite callSite)
    {
        _callSiteValidator?.ValidateCallSite(callSite);
    }

    private void OnResolve(Type serviceType, IServiceScope scope)
    {
        _callSiteValidator?.ValidateResolution(serviceType, scope, Root);
    }

    internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
    {
        if (_disposed)
        {
            ThrowHelper.ThrowObjectDisposedException();
        }
  
        Func<ServiceProviderEngineScope, object> realizedService = _realizedServices.GetOrAdd(serviceType, _createServiceAccessor);
        OnResolve(serviceType, serviceProviderEngineScope);
        DependencyInjectionEventSource.Log.ServiceResolved(this, serviceType);
        var result = realizedService.Invoke(serviceProviderEngineScope);
        System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceType));
        return result;
    }

    private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
    {
        ServiceCallSite callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
        if (callSite != null)
        {
            DependencyInjectionEventSource.Log.CallSiteBuilt(this, serviceType, callSite);
            OnCreate(callSite);

            // Optimize singleton case
            if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
            {
                object value = CallSiteRuntimeResolver.Instance.Resolve(callSite, Root);
                return scope => value;
            }

            return _engine.RealizeService(callSite);
        }

        return _ => null;
    }

    internal IServiceScope CreateScope()
    {
        if (_disposed)
        {
            ThrowHelper.ThrowObjectDisposedException();
        }

        return new ServiceProviderEngineScope(this, isRootScope: false);
    }

    private ServiceProviderEngine GetEngine()
    {
        ServiceProviderEngine engine;

#if NETFRAMEWORK || NETSTANDARD2_0
        engine = new DynamicServiceProviderEngine(this);
#else
        if (RuntimeFeature.IsDynamicCodeCompiled)
        {
            engine = new DynamicServiceProviderEngine(this);
        }
        else
        {
            engine = RuntimeServiceProviderEngine.Instance;
        }
#endif
        return engine;
    }
}

從上面的代碼中可以看出,在構建ServiceProvider時,主要是構造默認的根容器和采用哪種引擎來獲取服務,並且把服務的類型描述給構建好並緩存起來。我們再來看看GetService方法,里面的調用服務分為Root和非Root,也就是說,需要區分哪些是屬於根容器的,哪些不是屬於根容器的。在.Net中,默認情況下添加的添加的Singleton類型屬於Root,Scoped類型屬於Scope,Transient類型屬於Dispose,具體請看下面代碼:

internal struct ResultCache
{
    public ResultCache(ServiceLifetime lifetime, Type type, int slot)
    {
        switch (lifetime)
        {
            case ServiceLifetime.Singleton:
                Location = CallSiteResultCacheLocation.Root;
                break;
            case ServiceLifetime.Scoped:
                Location = CallSiteResultCacheLocation.Scope;
                break;
            case ServiceLifetime.Transient:
                Location = CallSiteResultCacheLocation.Dispose;
                break;
            default:
                Location = CallSiteResultCacheLocation.None;
                break;
        }
        Key = new ServiceCacheKey(type, slot);
    }
    public CallSiteResultCacheLocation Location { get; set; }
    public ServiceCacheKey Key { get; set; }
}
  1. 有了基本的了解之后,我們再來看通過GetService方法獲取對象實例,當類型是位於根容器時,會將根容器的實例做在緩存里面,而類型如果是Transient,那么則每次獲取時,都重新創建,不做緩存處理,至於Scoped方式的類型,稍后再說,我們先看下面代碼
internal sealed class CallSiteRuntimeResolver : CallSiteVisitor<RuntimeResolverContext, object>
{
  //對於Transient的類型實例
  protected override object VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
  {
      //直接構造類型實例,並記錄在dispose的集合中,等待容器被Dispose時,同時dispose掉
      return context.Scope.CaptureDisposable(VisitCallSiteMain(transientCallSite, context));
  }
  //獲取在根容器的對象實例
  protected override object VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
  {
      //對於已經做過緩存的服務,直接返回
      if (callSite.Value is object value)
      {
          return value;
      }
      var lockType = RuntimeResolverLock.Root;
      ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root;
      //對當前服務類型上鎖
      lock (callSite)
      {
          //相當於一個雙檢鎖,再查一遍
          if (callSite.Value is object resolved)
          {
              return resolved;
          }
          resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
          {
              Scope = serviceProviderEngine,
              AcquiredLocks = context.AcquiredLocks | lockType
          });
          serviceProviderEngine.CaptureDisposable(resolved);
          //將獲取到的實例做緩存,返回拿到的實例
          callSite.Value = resolved;
          return resolved;
      }
  }
}
  1. 而對於注入的Scope類型,我們知道針對每次請求下,針對每個類型的實例是一個,而在ServiceProviderEngineScope類中有一個CreateScope方法,而每次接收到請求時都會構建HttpContext實例,在構建的同時調用CreateScope方法,來構建新的容器作為本次請求的Scope容器,期間獲取的Scope類型的實例,都是在里面獲取,先做緩存再返回,保證這個作用域內的實例是一個,請看下面代碼:
internal sealed class CallSiteRuntimeResolver : CallSiteVisitor<RuntimeResolverContext, object>
{
    protected override object VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
    {
        //如果是根容器作用域,就在根容器中找,否則就在當前作用域容器中找
        return context.Scope.IsRootScope ?
            VisitRootCache(callSite, context) :
            VisitCache(callSite, context, context.Scope, RuntimeResolverLock.Scope);
    }

    private object VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
    {
        bool lockTaken = false;
        object sync = serviceProviderEngine.Sync;
        Dictionary<ServiceCacheKey, object> resolvedServices = serviceProviderEngine.ResolvedServices;
        if ((context.AcquiredLocks & lockType) == 0)
        {
            Monitor.Enter(sync, ref lockTaken);
        }

        try
        {
            //每次查找前,先判斷這個實例是否已經創建過,如果在本容器的緩存集合中存在就直接返回
            if (resolvedServices.TryGetValue(callSite.Cache.Key, out object resolved))
            {
                return resolved;
            }

            resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
            {
                Scope = serviceProviderEngine,
                AcquiredLocks = context.AcquiredLocks | lockType
            });
            serviceProviderEngine.CaptureDisposable(resolved);
            //將類型實例添加到緩存中
            resolvedServices.Add(callSite.Cache.Key, resolved);
            return resolved;
        }
        finally
        {
            if (lockTaken)
            {
                Monitor.Exit(sync);
            }
        }
    }
}

總結

通過源碼可以看出默認的依賴注入有以下特點:

  • 在一個Host中只能存在一個根容器,而其他容器(每次請求創建)都是從根容器中衍生出來的。
  • Scope的類型可能被提升到Singleton。
  • 對於Singleton的注入類型,都是存放在根容器中,並作緩存。
  • 對於Scoped的注入類型,大部分是存放在每次請求構建的容器中,並作緩存。
  • 對於Transient的注入類型,則不做緩存,每次訪問都構建出一個新的對象實例。

關於.Net中默認的依賴注入,上面的代碼也只是挑出重點的部分分享給大家,具體想看更多細節,讀者可以根據本篇博客直接看源碼,因為篇幅問題,實在不能貼太多的代碼,主要是把思路給大家說一下。


免責聲明!

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



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