源码解析.Net中DependencyInjection的实现
#前言 笔者的这篇文章和上篇文章思路一样,不注重依赖注入的使用方法,更加注重源码的实现,我尽量的表达清楚内容,让读者能够真正的学到东西。如果有不太清楚依赖注入是什么或怎么在.Net项目中使用的话,请点击这里,这是微软的官方文档,把用法介绍的很清晰了,相信你会有很大收获。那么废话不多说,咱们进入正题(可能篇幅有点长,耐心读完你会有收获的😁)。 #DependencyInjection类之间的关系 下图中只列举重要的类和接口(实际的类和接口有很多),里面的方法和属性也只列出重要的,这里只是让你有个大概的印象,看不懂没关系,继续往下读。 #源码解析
- 首先我们要知道,.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方法,来构建默认容器,而默认容器是作为根容器存在,全局只有一个。
- _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; } }
- 有了基本的了解之后,我们再来看通过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; } } }
- 而对于注入的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中默认的依赖注入,上面的代码也只是挑出重点的部分分享给大家,具体想看更多细节,读者可以根据本篇博客直接看源码,因为篇幅问题,实在不能贴太多的代码,主要是把思路给大家说一下。
- 【Android 源码解析】应用窗口Window的实现机制
- struts2拦截器的实现原理及源码解析
- Kylin源码解析——Cube构建过程中如何实现降维
- Toast实现源码解析
- Latent Dirichlet Allocation(LDA)主题模型算法实现及源码解析
- 智能聊天机器人实现 源码+解析
- netty源码解解析(4.0)-13 Channel NIO实现: 关闭和清理
- Latent Dirichlet Allocation(LDA)主题模型算法实现及源码解析
- jsp基于XML实现用户登录与注册的实例解析(附源码)
- 深入解析Apache Mina源码(1)——Mina的过滤器机制实现
- 经过一周的挣扎,目前天气预报抓取解析插入数据库的功能已经大致实现,附项目源码
- iOS弹幕(源码)实现原理解析
- 02.Spring IOC源码深度解析之容器的基本实现
- 智能聊天机器人实现 源码+解析
- Android 属性动画 源码解析 深入了解其内部实现
- fastjson深度源码解析- 序列化(六) - json特定序列化实现解析
- OpenStack建立实例完整过程源码详细分析(12)----依据AMQP通信架构实现消息发送机制解析之一
- 智能聊天机器人实现 源码+解析
- sharding-jdbc源码解析之spring集成分片构造实现
- 智能聊天机器人实现 源码+解析