阅读 127

源码解析.Net中DependencyInjection的实现

前言

笔者的这篇文章和上篇文章思路一样,不注重依赖注入的使用方法,更加注重源码的实现,我尽量的表达清楚内容,让读者能够真正的学到东西。如果有不太清楚依赖注入是什么或怎么在.Net项目中使用的话,请,这是微软的官方文档,把用法介绍的很清晰了,相信你会有很大收获。那么废话不多说,咱们进入正题(可能篇幅有点长,耐心读完你会有收获的??)。

DependencyInjection类之间的关系

下图中只列举重要的类和接口(实际的类和接口有很多),里面的方法和属性也只列出重要的,这里只是让你有个大概的印象,看不懂没关系,继续往下读。

源码解析

  1. 首先我们要知道,.Net程序在启动的时候会构建一个Host(Host.Build().Run()),在其Build方法中来构建容器(只构建根容器,下面会解释),具体在哪构建容器,源码如下图:
public class HostBuilder : IHostBuilder
{
    private List> _configureHostConfigActions = new List>();
    public IHostBuilder ConfigureAppConfiguration(Action 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();
        if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuiltEventName))
        {
            Write(diagnosticListener, hostBuiltEventName, host);
        }

        return host;
    }
    
     private void CreateServiceProvider()
     {
        //构建服务集合
        var services = new ServiceCollection();
        services.AddSingleton(_hostingEnvironment);
        services.AddSingleton(_hostingEnvironment);
        services.AddSingleton(_hostBuilderContext);
        services.AddSingleton(_ => _appConfiguration);
        services.AddSingleton(s => (IApplicationLifetime)s.GetService());
        services.AddSingleton();
        AddLifetime(services);
        services.AddSingleton(_ =>
        {
            return new Internal.Host(_appServices,
                _hostingEnvironment,
                _defaultProvider,
                _appServices.GetRequiredService(),
                _appServices.GetRequiredService>(),
                _appServices.GetRequiredService(),
                _appServices.GetRequiredService>());
        });
        services.AddOptions().Configure(options => { options.Initialize(_hostConfiguration); });
        services.AddLogging();
        //加载StartUp里面的ConfigService方法
        foreach (Action 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();
    }
}

从上面可以看出,_serviceProviderFactory实际上就是DefaultServiceProviderFactory对象实例,它通过调用自己的CreateServiceProvider方法,来构建默认容器,而默认容器是作为根容器存在,全局只有一个。

  1. _serviceProviderFactory.CreateBuilder(services)方法只是简单的转换(把IServiceCollection转化成Object),为什么他要返回object呢?因为在.net中默认实现是个IServiceCollection,那么如果要是需要自定义容器的话,则需要通过自己的ProviderFactory返回自己的Builder,来生成容器,在这里就不细讲了,以后其他文章会详细说下怎么自定义一个IOC,这里只讲默认实现,接下来我们具体讲它是怎么生成容器服务的,请看CreateServiceProvider方法实现:
//.net提供的默认的ProviderFactory
public class DefaultServiceProviderFactory : IServiceProviderFactory
{
    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> _createServiceAccessor;
    //服务提供的引擎
    internal ServiceProviderEngine _engine;
    //是否已被释放
    private bool _disposed;
    //对每种type做了一个缓存,来提高效率
    private ConcurrentDictionary> _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 serviceDescriptors, ServiceProviderOptions options)
    {
        //根容器就是本身,并且IsRootScope是true标识
        Root = new ServiceProviderEngineScope(this, isRootScope: true); 
        //获取用那种引擎来获取服务
        _engine = GetEngine(); 
        //获取服务时调用的函数
        _createServiceAccessor = CreateServiceAccessor;
        _realizedServices = new ConcurrentDictionary>();
        //主要是生成服务的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 exceptions = null;
            foreach (ServiceDescriptor serviceDescriptor in serviceDescriptors)
            {
                try
                {
                    ValidateService(serviceDescriptor);
                }
                catch (Exception e)
                {
                    exceptions = exceptions ?? new List();
                    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 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 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
{
  //对于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
{
    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 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中默认的依赖注入,上面的代码也只是挑出重点的部分分享给大家,具体想看更多细节,读者可以根据本篇博客直接看源码,因为篇幅问题,实在不能贴太多的代码,主要是把思路给大家说一下。

原文:https://www.cnblogs.com/snailZz/p/15208217.html

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐