前言

笔者的这篇文章和上篇文章思路一样,不注重依赖注入的使用方法,更加注重源码的实现,我尽量的表达清楚内容,让读者能够真正的学到东西。如果有不太清楚依赖注入是什么或怎么在.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中默认的依赖注入,上面的代码也只是挑出重点的部分分享给大家,具体想看更多细节,读者可以根据本篇博客直接看源码,因为篇幅问题,实在不能贴太多的代码,主要是把思路给大家说一下。

源码解析.Net中DependencyInjection的实现的更多相关文章

  1. Spark 源码解析 : DAGScheduler中的DAG划分与提交

    一.Spark 运行架构 Spark 运行架构如下图: 各个RDD之间存在着依赖关系,这些依赖关系形成有向无环图DAG,DAGScheduler对这些依赖关系形成的DAG,进行Stage划分,划分的规 ...

  2. 源码解析.Net中IConfiguration配置的实现

    前言 关于IConfituration的使用,我觉得大部分人都已经比较熟悉了,如果不熟悉的可以看这里.因为本篇不准备讲IConfiguration都是怎么使用的,但是在源码部分的解读,网上资源相对少一 ...

  3. 源码解析.Net中Middleware的实现

    前言 本篇继续之前的思路,不注重用法,如果还不知道有哪些用法的小伙伴,可以点击这里,微软文档说的很详细,在阅读本篇文章前,还是希望你对中间件有大致的了解,这样你读起来可能更加能够意会到意思.废话不多说 ...

  4. 源码解析.Net中Host主机的构建过程

    前言 本篇文章着重讲一下在.Net中Host主机的构建过程,依旧延续之前文章的思路,着重讲解其源码,如果有不知道有哪些用法的同学可以点击这里,废话不多说,咱们直接进入正题 Host构建过程 下图是我自 ...

  5. 源码解析C#中PriorityQueue(优先级队列)的实现

    前言 前段时间看到有大佬对.net 6.0新出的PriorityQueue(优先级队列)数据结构做了解析,但是没有源码分析,所以本着探究源码的心态,看了看并分享出来.它不像普通队列先进先出(FIFO) ...

  6. multiprocessing 源码解析 更新中......

    一.参考链接 1.源码包下载·链接:   https://pypi.org/search/?q=multiprocessing+ 2.源码包 链接:https://pan.baidu.com/s/1j ...

  7. 源码解析Android中View的measure量算过程

    Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文< Android中View ...

  8. spring boot 源码解析52-actuate中MVCEndPoint解析

    今天有个别项目的jolokia的endpoint不能访问,调试源码发现:endpoint.enabled的开关导致的. 关于Endpoint, <Springboot Endpoint之二:En ...

  9. Spring源码解析-Advice中的Adapter模式

    在spring中与通知相关的类有: 以Advice结尾的通知接口    MethodBeforeAdvice    AfterReturningAdvice   ThrowsAdvice 以Inter ...

随机推荐

  1. 接口开发---basic auth接口认证

    开发中遇到了basic auth来认证的案例,这里总结一下: Basic Auth简单点说明就是每次请求API时都提供用户的username和password.[base64encode(userna ...

  2. Tree Widget -- 基本方法

    Tree Widget这个空间类似于一种表格的形式,是一种树状结构 效果图: 第一步:打开designer.exe,拖动一个Tree Widget空间到主窗口上 第二步:双击Tree Widget,添 ...

  3. maven 与profile,resources,properties 关系

    top 的 pom.xml 看<profiles>的标签 <profiles> <!--dat环境--> <profile> <id>DAT ...

  4. 远程访问Jupyter Notebook的两种方式:命令行和配置文件

    远程访问Jupyter Notebook的两种方式:命令行和配置文件 相关配置:Ubuntu 16.04服务器,本地Win10,使用了Xshell,Xftp工具. 相关配置主要分为三步: 服务器上的J ...

  5. 20初识前端HTML(1)

    1 .HTML 1.1 网页的组成 文字 图片 链接 等元素构成.除了这些元素之外 网页中还可以包含音频 视频 等 1.2 WEB前端开发的流程 现在主流的开发流程: 前后端分离的开发模式. 美工:p ...

  6. python grequest模块使用备忘录

    手里上有一批链接,需要检查他们是否已经被删除.本来是想用多线程的,但是考虑了下一个是实现起来稍繁琐.而且性能不理想,单机基本超过10线程基本上就没有太多增益了. 所以考虑了下,还是决定用异步IO. 在 ...

  7. C++STL—string类

    string容器 1.1 string容器的基本概念 string容器是一个类 这个容器中有一个指针,指针维护了一个数组 string容器提供copy.find.insert.replace等等功能 ...

  8. 第5篇-调用Java方法后弹出栈帧及处理返回结果

    在前一篇 第4篇-JVM终于开始调用Java主类的main()方法啦 介绍了通过callq调用entry point,不过我们并没有看完generate_call_stub()函数的实现.接下来在ge ...

  9. Java代码编写、代码优化技巧总结

    随着工作经验的积累,在代码编写和优化方面,个人的心得体会总结以及有些从网上或书本中看到的有用技巧 1. 判断何时使用keySet()和entrySet() 获取Map 的key 和value 当循环中 ...

  10. 课程设计- 基于ssm的捐赠物资分配管理系统 && 基于java的申请救援管理系统

    课程设计- 基于ssm的捐赠物资分配管理系统 && 基于java的申请救援管理系统 注意:该项目只展示部分功能,如需了解,评论区咨询即可. 1.开发环境 开发语言:Java 后台框架: ...